Created
January 11, 2024 17:32
-
-
Save 7dir/d45c2702a2f235a97e0410e0777940a9 to your computer and use it in GitHub Desktop.
Revisions
-
7dir created this gist
Jan 11, 2024 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,4 @@ <div class="container"> <canvas class="illo"></canvas> <p>Click & drag to rotate</p> </div> This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,688 @@ // Made with Zdog var BokehShape = Zdog.Shape.subclass({ bokehSize: 5, bokehLimit: 64, }); BokehShape.prototype.updateBokeh = function() { // bokeh 0 -> 1 this.bokeh = Math.abs( this.sortValue ) / this.bokehLimit; this.bokeh = Math.max( 0, Math.min( 1, this.bokeh ) ); return this.bokeh; }; BokehShape.prototype.getLineWidth = function() { return this.stroke + this.bokehSize * this.bokeh * this.bokeh; }; BokehShape.prototype.getBokehAlpha = function() { var alpha = 1 - this.bokeh; alpha *= alpha; return alpha * 0.8 + 0.2; }; BokehShape.prototype.renderCanvasDot = function( ctx ) { this.updateBokeh(); ctx.globalAlpha = this.getBokehAlpha(); // set opacity Zdog.Shape.prototype.renderCanvasDot.apply( this, arguments ); ctx.globalAlpha = 1; // reset }; BokehShape.prototype.renderPath = function( ctx, renderer ) { this.updateBokeh(); // set opacity if ( renderer.isCanvas ) { ctx.globalAlpha = this.getBokehAlpha(); } Zdog.Shape.prototype.renderPath.apply( this, arguments ); // reset opacity if ( renderer.isCanvas ) { ctx.globalAlpha = 1; } }; var TAU = Zdog.TAU; function makeMadeline( isGood, colors, options ) { var rotor = new Zdog.Anchor( options ); var body = new Zdog.Group({ addTo: rotor, rotate: { x: -TAU/8 }, translate: { z: -48 }, updateSort: true, }); var head = new Zdog.Anchor({ addTo: body, translate: { y: -11, z: -2 }, rotate: { x: TAU/8 }, }); // face var face = new Zdog.Ellipse({ diameter: 6, addTo: head, translate: { z: 4 }, stroke: 8, color: colors.skin, }); var eyeGroup = new Zdog.Group({ addTo: face, translate: { z: face.stroke/2 - 0.5 }, }); // eyes [ -1, 1 ].forEach( function( xSide ) { // cheek blush if ( isGood ) { new Zdog.Ellipse({ width: 2, height: 1.3, addTo: eyeGroup, translate: { x: 4.5*xSide, y: 3, z: -1 }, rotate: { y: -TAU/16*xSide }, stroke: 1, color: '#FA8', fill: true, }); } var eyeX = 3.5*xSide; // eye new Zdog.Ellipse({ width: 0.75, height: 1.5, addTo: eyeGroup, color: colors.eye, translate: { x: eyeX }, stroke: 2, fill: true, }); // eye brow new Zdog.Ellipse({ addTo: eyeGroup, height: 3, width: 1.2, quarters: 2, translate: { x: eyeX, y: -3 }, rotate: { z: -TAU/4 + 0.15*xSide * (isGood ? 1 : -1) }, color: colors.hair, stroke: 1, fill: false, closed: true, }); }); // hair ball new Zdog.Shape({ path: [ { x: -1 }, { x: 1 }, { z: -4 }, ], addTo: head, translate: { y: -4, z: -1 }, stroke: 18, color: colors.hair, }); var bang = new Zdog.Shape({ path: [ {}, { arc: [ { z: 4, y: 4 }, { z: 0, y: 8 }, ]}, ], addTo: head, translate: { x: 2, y: -7.5, z: 6 }, rotate: { x: 0.5, z: -0.5 }, stroke: 4, color: colors.hair, closed: false, }); bang.copy({ translate: { x: 5, y: -6, z: 5 }, rotate: { x: -0.3, z: -0.5 }, }); bang.copy({ translate: { x: 5, y: -6, z: 3 }, rotate: { y: -0.7, z: -1 }, }); // left side bang.copy({ translate: { x: -2, y: -7.5, z: 6 }, rotate: { x: 0, z: TAU/16*6 }, }); bang.copy({ translate: { x: -5, y: -6, z: 5 }, rotate: { x: 0, z: TAU/4 }, }); bang.copy({ translate: { x: -5, y: -6, z: 3 }, rotate: { y: 0.7, z: 1 }, }); // hair cover new Zdog.Shape({ path: [ { x: -3 }, { x: 3 }, ], addTo: head, stroke: 7, translate: { y: -8, z: 5 }, color: colors.hair, }); // trail locks var trailLock = new Zdog.Shape({ path: [ { y: -4, z: 0 }, { bezier: [ { y: -10, z: -14 }, { y: 0, z: -16 }, { y: 0, z: -26 } ]}, ], addTo: head, translate: { z: -4, y: 0 }, stroke: 10, color: colors.hair, closed: false, }); trailLock.copy({ translate: { x: -3, z: -4 }, rotate: { z: -TAU/8 }, stroke: 8, }); trailLock.copy({ translate: { x: 3, z: -4 }, rotate: { z: TAU/8 }, stroke: 8, }); trailLock.copy({ translate: { y: 2 }, // rotate: { z: TAU/2 }, scale: { y: 0.5 }, stroke: 8, }); // ----- torso ----- // // 2nd rib var torsoRib = new Zdog.Ellipse({ width: 12, height: 10, addTo: body, rotate: { x: -TAU/4 }, translate: { y: -1 }, stroke: 6, color: colors.parkaLight, fill: true, }); // neck rib torsoRib.copy({ width: 6, height: 6, translate: { y: -5 }, }); // 3rd rib torsoRib.copy({ translate: { y: 3 }, }); // 4th rib torsoRib.copy({ translate: { y: 7 }, color: colors.parkaDark, }); // waist new Zdog.Ellipse({ width: 10, height: 8, addTo: body, rotate: { x: -TAU/4 }, translate: { y: 11 }, stroke: 4, color: colors.tight, fill: true, }); // arms [ -1, 1 ].forEach( function( xSide ) { var isLeft = xSide == 1; // shoulder ball new Zdog.Shape({ addTo: body, stroke: 6, translate: { x: 6*xSide, y: -5, z: -1 }, color: colors.parkaLight, }); var shoulderJoint = new Zdog.Anchor({ addTo: body, translate: { x: 9*xSide, y: -3, z: -2 }, rotate: isLeft ? { x: TAU/8*3, z: -TAU/32 } : { z: TAU/16*2, x: -TAU/16*2 }, }); // top shoulder rib var armRib = new Zdog.Ellipse({ diameter: 2, rotate: { x: -TAU/4 }, addTo: shoulderJoint, translate: { x: 0*xSide }, stroke: 6, color: colors.parkaLight, fill: true, }); armRib.copy({ translate: { y: 4 }, }); var elbowJoint = new Zdog.Anchor({ addTo: shoulderJoint, translate: { y: 8 }, rotate: isLeft ? {} : { z: TAU/8 }, }); armRib.copy({ addTo: elbowJoint, translate: { x: 0, y: 0 }, }); armRib.copy({ addTo: elbowJoint, translate: { y: 4 }, color: colors.parkaDark, }); // hand new Zdog.Shape({ addTo: elbowJoint, translate: { y: 9, z: -1 }, stroke: 8, color: colors.skin, }); // ----- legs ----- // var knee = { y: 7 }; var thigh = new Zdog.Shape({ path: [ { y: 0 }, knee ], addTo: body, translate: { x: 4*xSide, y: 13 }, rotate: isLeft ? {} : { x: TAU/16*3, z: TAU/16 }, stroke: 8, color: colors.tight, }); var shin = new Zdog.Shape({ path: [ { y: 0 }, { y: 8 } ], addTo: thigh, stroke: 6, translate: knee, rotate: isLeft ? {} : { x: -TAU/16*5 }, color: colors.tight, }); }); // butt new Zdog.Shape({ path: [ { x: -3 }, { x: 3 }, ], visible: false, addTo: body, translate: { y: 11, z: -2 }, stroke: 8, color: colors.tight, }); } window.makeBird = function( options ) { var spin = options.spin || 0; var arrow = new Zdog.Anchor({ addTo: options.addTo, scale: 2/3, rotate: { z: spin }, }); var bird = new Zdog.Group({ addTo: arrow, translate: { x: 87 }, rotate: { x: -spin }, }); // bird body new Zdog.Shape({ path: [ { x: -3, y: 0 }, { arc: [ { x: -2, y: 1.5 }, { x: 0, y: 1.5 }, ]}, { arc: [ { x: 2, y: 1.5 }, { x: 2, y: 0 }, ]}, ], addTo: bird, translate: { x: 0.5 }, stroke: 3, color: options.color, fill: true, }); // bird head new Zdog.Shape({ translate: { x: 4, y: -1 }, addTo: bird, stroke: 4, color: options.color, }); // beak new Zdog.Shape({ path: [ { x: 0, y: -1 }, { x: 3, y: 0 }, { x: 0, y: 1 }, ], addTo: bird, translate: { x: 5, y: -1 }, stroke: 1, color: options.color, fill: true, }); // tail feather new Zdog.Shape({ path: [ { x: -3, z: -2 }, { x: 0, z: 0 }, { x: -3, z: 2 }, ], addTo: bird, translate: { x: -4, y: 0 }, stroke: 2, color: options.color, fill: true, }); var wing = new Zdog.Shape({ path: [ { x: 3, y: 0 }, { x: -1, y: -9 }, { arc: [ { x: -5, y: -4 }, { x: -3, y: 0 }, ]}, ], addTo: bird, translate: { z: -1.5}, rotate: { x: TAU/8 }, stroke: 1, color: options.color, fill: true, }); wing.copy({ translate: { z: 1.5}, scale: { z: -1 }, rotate: { x: -TAU/8 }, }); }; // -------------------------- demo -------------------------- // var illoElem = document.querySelector('.illo'); var w = 160; var h = 160; var minWindowSize = Math.min( window.innerWidth, window.innerHeight ); var zoom = Math.min( 5, Math.floor( minWindowSize / w ) ); illoElem.setAttribute( 'width', w * zoom ); illoElem.setAttribute( 'height', h * zoom ); var isSpinning = true; var TAU = Zdog.TAU; var illo = new Zdog.Illustration({ element: illoElem, zoom: zoom, rotate: { y: -TAU/4 }, dragRotate: true, onDragStart: function() { isSpinning = false; }, }); var madColor = { skin: '#FD9', hair: '#D53', parkaLight: '#67F', parkaDark: '#35D', tight: '#742', eye: '#333', }; var badColor = { skin: '#EBC', hair: '#D4B', parkaLight: '#85A', parkaDark: '#527', tight: '#412', eye: '#D02', }; var glow = 'hsla(60, 100%, 80%, 0.3)'; var featherGold = '#FE5'; // -- illustration shapes --- // makeMadeline( true, madColor, { addTo: illo, }); makeMadeline( false, badColor, { addTo: illo, rotate: { y: TAU/2 }, }); // ----- feather ----- // var feather = new Zdog.Group({ addTo: illo, rotate: { y: -TAU/4 }, }); ( function() { var featherPartCount = 8; var radius = 12; var angleX = (TAU/featherPartCount) / 2; var sector = (TAU * radius)/2 / featherPartCount; for ( var i=0; i < featherPartCount; i++ ) { var curve = Math.cos( (i/featherPartCount) * TAU*3/4 + TAU*1/4 ); var x = 4 - curve*2; var y0 = sector/2; // var y2 = -sector/2; var isLast = i == featherPartCount - 1; var y3 = isLast ? sector * -1 : -y0; var z1 = -radius + 2 + curve*-1.5; var z2 = isLast ? -radius : -radius; var barb = new Zdog.Shape({ path: [ { x: 0, y: y0, z: -radius }, { x: x, y: -sector/2, z: z1 }, { x: x, y: -sector*3/4, z: z1 }, { x: 0, y: y3, z: z2 }, ], addTo: feather, rotate: { x: angleX * -i + TAU/8 }, stroke: 1, color: featherGold, fill: true, }); barb.copy({ scale: { x: -1 }, }); } // rachis var rachis = new Zdog.Ellipse({ addTo: feather, diameter: radius*2, quarters: 2, rotate: { y: -TAU/4 }, stroke: 2, color: featherGold, }); rachis.copy({ stroke: 8, color: glow, rotate: { y: -TAU/4, x: -0.5 } }); })(); // ----- rods ----- // ( function() { var rodCount = 14; for ( var i=0; i < rodCount; i++ ) { var zRotor = new Zdog.Anchor({ addTo: illo, rotate: { z: TAU/rodCount * i }, }); var y0 = 32; var y1 = y0 + 2 + Math.random()*24; new BokehShape({ path: [ { y: y0 }, { y: y1 }, ], addTo: zRotor, rotate: { x: ( Math.random() * 2 - 1 ) * TAU/8 }, color: madColor.skin, stroke: 1, bokehSize: 6, bokehLimit: 70, }); } })(); // dots ( function() { var dotCount = 64; for ( var i=0; i < dotCount; i++ ) { var yRotor = new Zdog.Anchor({ addTo: illo, rotate: { y: TAU/dotCount * i }, }); new BokehShape({ path: [ { z: 40*(1 - Math.random()*Math.random()) + 32 }, ], addTo: yRotor, rotate: { x: ( Math.random() * 2 - 1 ) * TAU*3/16 }, color: badColor.skin, stroke: 1 + Math.random(), bokehSize: 6, bokehLimit: 74, }); } })(); // ----- birds ----- // var birdRotor = new Zdog.Anchor({ addTo: illo, rotate: { y: TAU*-1/8 }, }); makeBird({ addTo: birdRotor, color: madColor.parkaLight, spin: TAU/2, }); makeBird({ addTo: birdRotor, color: featherGold, spin: -TAU * 3/8, }); makeBird({ addTo: birdRotor, color: 'white', spin: -TAU/4, }); makeBird({ addTo: birdRotor, color: madColor.hair, spin: -TAU/8, }); makeBird({ addTo: birdRotor, color: madColor.parkaDark, spin: TAU/8, }); // -- animate --- // var isSpinning = true; var rotateSpeed = -TAU/60; var xClock = 0; var then = new Date() - 1/60; function animate() { update(); illo.renderGraph(); requestAnimationFrame( animate ); } animate(); // -- update -- // function update() { var now = new Date(); var delta = now - then; // auto rotate if ( isSpinning ) { var theta = rotateSpeed/60 * delta * -1; illo.rotate.y += theta; xClock += theta/4; illo.rotate.x = Math.sin( xClock ) * TAU/12; } illo.updateGraph(); then = now; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1 @@ <script src="https://unpkg.com/zdog@1/dist/zdog.dist.js"></script> This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,19 @@ html { height: 100%; } body { min-height: 100%; margin: 0; display: flex; align-items: center; justify-content: center; background: #435; color: white; font-family: sans-serif; text-align: center; } canvas { display: block; margin: 0 auto; cursor: move; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,21 @@ Zdog - Celeste snowglobe ------------------------ [Made with Zdog](https://zzz.dog) Been playing a lot of Celeste. In the eye of the storm with Madeline. Special sauce includes bokeh shapes for the snowflakes and light rods, which grow & fade out as the move further from the scene center. [View more round 3D Pens](https://codepen.io/desandro/pens/tags/?selected_tag=round3d) --- Built with my own vanilla JS (still rocking that ES5) using the <canvas> drawing API. No WebGL. Shapes are rendered with thick lineWidth, giving the illusion of 3D form. A sphere has the same 2D contour as a flat circle. A 3D pill has the same contour of a 2D pill. That's the trick. This is the best engine to render hot dogs and burger patties. [3D code comes from this Khan Academy lesson](https://www.khanacademy.org/computing/computer-programming/programming-games-visualizations/programming-3d-shapes/a/rotating-3d-shapes). It's great because it only uses simple trig, and no matrix mindbenders. Yeah it's buggy as heck, but that's the _charm_. It has no concept of intersecting or clipping. Shapes are either rendered before or after one another. Two shapes that occupy the same space will pop over one another when rotated. A [Pen](https://codepen.io/desandro/pen/RQeYYp) by [Dave DeSandro](https://codepen.io/desandro) on [CodePen](https://codepen.io). [License](https://codepen.io/license/pen/RQeYYp).