Toggle navigation
Sign Up
Log In
Explore
Works
Folders
Tools
Collections
Artists
Groups
Groups
Topics
Tasks
Tasks
Jobs
Teams
Jobs
Recommendation
More Effects...
JS
/*jshint unused: false, undef: true */ /*global blockSize: false */ // ----- utils ----- // // extends objects function extend( a, b ) { for ( var prop in b ) { a[ prop ] = b[ prop ]; } return a; } function modulo( num, div ) { return ( ( num % div ) + div ) % div; } function normalizeAngle( angle ) { return modulo( angle, Math.PI * 2 ); } function getDegrees( angle ) { return angle * ( 180 / Math.PI ); } // -------------------------- -------------------------- // function line( ctx, a, b ) { ctx.beginPath(); ctx.moveTo( a.x, a.y ); ctx.lineTo( b.x, b.y ); ctx.stroke(); ctx.closePath(); } /*jshint browser: true, undef: true, unused: true */ // -------------------------- vector -------------------------- // function Vector( x, y ) { this.x = x || 0; this.y = y || 0; } Vector.prototype.set = function( v ) { this.x = v.x; this.y = v.y; }; Vector.prototype.setCoords = function( x, y ) { this.x = x; this.y = y; } Vector.prototype.add = function( v ) { this.x += v.x; this.y += v.y; }; Vector.prototype.subtract = function( v ) { this.x -= v.x; this.y -= v.y; }; Vector.prototype.scale = function( s ) { this.x *= s; this.y *= s; }; Vector.prototype.multiply = function( v ) { this.x *= v.x; this.y *= v.y; }; // custom getter whaaaaaaat Object.defineProperty( Vector.prototype, 'magnitude', { get: function() { return Math.sqrt( this.x * this.x + this.y * this.y ); } }); Vector.prototype.equals = function ( v ) { return this.x == v.x && this.y == v.y; }; Vector.prototype.zero = function() { this.x = 0; this.y = 0; }; Vector.prototype.block = function( size ) { this.x = Math.floor( this.x / size ); this.y = Math.floor( this.y / size ); }; Object.defineProperty( Vector.prototype, 'angle', { get: function() { return normalizeAngle( Math.atan2( this.y, this.x ) ); } }); // ----- class functions ----- // // return new vectors Vector.subtract = function( a, b ) { return new Vector( a.x - b.x, a.y - b.y ); }; Vector.add = function( a, b ) { return new Vector( a.x + b.x, a.y + b.y ); }; Vector.copy = function( v ) { return new Vector( v.x, v.y ); }; Vector.isSame = function( a, b ) { return a.x == b.x && a.y == b.y; }; Vector.getDistance = function( a, b ) { var dx = a.x - b.x; var dy = a.y - b.y; return Math.sqrt( dx * dx + dy * dy ); }; Vector.addDistance = function( vector, distance, angle ) { var x = vector.x + Math.cos( angle ) * distance; var y = vector.y + Math.sin( angle ) * distance; return new Vector( x, y ); }; // -------------------------- -------------------------- // // -------------------------- Particle -------------------------- // function Particle( x, y ) { this.position = new Vector( x, y ); this.previousPosition = new Vector( x, y ); } Particle.prototype.update = function( friction, gravity ) { var velocity = Vector.subtract( this.position, this.previousPosition ); // friction velocity.scale( friction ); this.previousPosition.set( this.position ); this.position.add( velocity ); this.position.add( gravity ); }; // -------------------------- -------------------------- // Particle.prototype.render = function( ctx ) { // big circle ctx.fillStyle = 'hsla(0, 0%, 10%, 0.5)'; circle( ctx, this.position.x, this.position.y, 4 ); // dot // ctx.fillStyle = 'hsla(0, 100%, 50%, 0.5)'; // circle( this.position.x, this.position.y, 5 ); }; function circle( ctx, x, y, radius ) { ctx.beginPath(); ctx.arc( x, y, radius, 0, Math.PI * 2 ); ctx.fill(); ctx.closePath(); } // -------------------------- -------------------------- // function StickConstraint( particleA, particleB, distance ) { this.particleA = particleA; this.particleB = particleB; if ( distance ) { this.distance = distance; } else { var delta = Vector.subtract( particleA.position, particleB.position ); this.distance = delta.magnitude; } this.distanceSqrd = this.distance * this.distance; } StickConstraint.prototype.update = function() { var delta = Vector.subtract( this.particleA.position, this.particleB.position ); var mag = delta.magnitude; var scale = ( this.distance - mag ) / mag * 0.5; delta.scale( scale ); this.particleA.position.add( delta ); this.particleB.position.subtract( delta ); }; StickConstraint.prototype.render = function( ctx ) { ctx.strokeStyle = 'hsla(200, 100%, 50%, 0.5)'; ctx.lineWidth = 2; line( ctx, this.particleA.position, this.particleB.position ); }; // -------------------------- -------------------------- // function PinConstraint( particle, position ) { this.particle = particle; this.position = position; } PinConstraint.prototype.update = function() { this.particle.position.set( this.position ); }; PinConstraint.prototype.render = function() {}; // -------------------------- -------------------------- // function SpringAngleConstraint( particleA, particleB, strength, angle ) { this.particleA = particleA; this.particleB = particleB; this.strength = strength; if ( angle === undefined ) { var delta = Vector.subtract( particleB.position, particleA.position ); this.angle = delta.angle; } else { this.angle = angle; } } SpringAngleConstraint.prototype.update = function() { var positionA = this.particleA.position; var positionB = this.particleB.position; var delta = Vector.subtract( positionB, positionA ); var deltaAngle = delta.angle; var angleDiff = normalizeAngle( this.angle - deltaAngle ); angleDiff = angleDiff > Math.PI ? angleDiff - Math.PI * 2 : angleDiff; var springAngle = deltaAngle + Math.PI / 2; var springForce = new Vector( Math.cos( springAngle ), Math.sin( springAngle ) ); springForce.scale( angleDiff * this.strength * Math.PI * 2 ); this.particleB.position.add( springForce ); }; SpringAngleConstraint.prototype.render = function( ctx ) { var end = Vector.addDistance( this.particleA.position, 50, this.angle ); ctx.strokeStyle = 'hsla(0, 0%, 50%, 0.5)'; line( ctx, this.particleA.position, end ); }; // -------------------------- -------------------------- // function ChainLinkConstraint( particleA, particleB, distance, shiftEase ) { this.particleA = particleA; this.particleB = particleB; this.distance = distance; this.distanceSqrd = distance * distance; this.shiftEase = shiftEase === undefined ? 0.85 : shiftEase; } ChainLinkConstraint.prototype.update = function() { var delta = Vector.subtract( this.particleA.position, this.particleB.position ); var deltaMagSqrd = delta.x * delta.x + delta.y * delta.y; if ( deltaMagSqrd <= this.distanceSqrd ) { return; } var newPosition = Vector.addDistance( this.particleA.position, this.distance, delta.angle + Math.PI ); var shift = Vector.subtract( newPosition, this.particleB.position ); shift.scale( this.shiftEase ); this.particleB.previousPosition.add( shift ); this.particleB.position.set( newPosition ); }; // -------------------------- -------------------------- // function Ribbon( props ) { extend( this, props ); // create particles this.particles = []; this.constraints = []; this.controlParticle = new Particle( this.controlPoint.x, this.controlPoint.y ); var pin = new PinConstraint( this.controlParticle, this.controlPoint ); this.constraints.push( pin ); var x = this.controlPoint.x; for ( var i=0; i < this.sections; i++ ) { var y = this.controlPoint.y + this.sectionLength * i; var particle = new Particle( x, y ); this.particles.push( particle ); // create links var linkParticle = i === 0 ? this.controlParticle : this.particles[ i-1 ]; var link = new ChainLinkConstraint( linkParticle, particle, this.sectionLength, this.chainLinkShiftEase ); this.constraints.push( link ); } } Ribbon.prototype.update = function() { var i, len; for ( i=0, len = this.particles.length; i < len; i++ ) { this.particles[i].update( this.friction, this.gravity ); } for ( i=0, len = this.constraints.length; i < len; i++ ) { this.constraints[i].update(); } for ( i=0, len = this.constraints.length; i < len; i++ ) { this.constraints[i].update(); } }; Ribbon.prototype.addBreeze = function( v ) { for ( var i=0, len = this.particles.length; i < len; i++ ) { this.particles[i].position.add( v ); } }; Ribbon.prototype.render = function( ctx ) { ctx.strokeStyle = '#d916d9'; ctx.lineWidth = this.width; ctx.lineCap = 'butt'; ctx.lineJoin = 'round'; ctx.beginPath(); ctx.moveTo( this.controlParticle.x, this.controlParticle.y ); for ( var i=0, len = this.particles.length; i < len; i++ ) { var particle = this.particles[i]; ctx.lineTo( particle.position.x, particle.position.y ); } ctx.stroke(); ctx.closePath(); ctx.lineWidth = 1; }; // -------------------------- -------------------------- // // x, y // angle // springStrength // curl // segmentLength // friction // gravity // movementStrength function Follicle( props ) { extend( this, props ); delete this.x; delete this.y; this.particleA = new Particle( props.x, props.y ); var positionB = Vector.addDistance( this.particleA.position, this.segmentLength, this.angle ); this.particleB = new Particle( positionB.x, positionB.y ); this.stick0 = new StickConstraint( this.particleA, this.particleB ); this.springAngle0 = new SpringAngleConstraint( this.particleA, this.particleB, this.springStrength, this.angle ); var angle1 = this.angle + this.curl; var positionC = Vector.addDistance( this.particleB.position, this.segmentLength, angle1 ); this.particleC = new Particle( positionC.x, positionC.y ); this.stick1 = new StickConstraint( this.particleB, this.particleC ); this.springAngle1 = new SpringAngleConstraint( this.particleB, this.particleC, this.springStrength, angle1 ); this.controlPoint = new Vector( props.x, props.y ); this.pin = new PinConstraint( this.particleA, this.controlPoint ); } Follicle.prototype.update = function() { this.particleA.update( this.friction, this.gravity ); this.particleB.update( this.friction, this.gravity ); this.particleC.update( this.friction, this.gravity ); this.stick0.update(); this.springAngle0.update(); // update springAngle1's angle var delta = Vector.subtract( this.particleB.position, this.particleA.position ); this.springAngle1.angle = delta.angle + this.curl; this.pin.update(); this.stick1.update(); this.springAngle1.update(); }; Follicle.prototype.move = function( movement ) { movement = Vector.copy( movement ); this.controlPoint.add( movement ); movement.scale( this.movementStrength ); this.particleB.position.add( movement ); this.particleC.position.add( movement ); this.particleB.previousPosition.add( movement ); this.particleC.previousPosition.add( movement ); }; Follicle.prototype.render = function( ctx ) { ctx.lineWidth = 46; ctx.strokeStyle = '#333'; ctx.lineCap = 'round'; ctx.beginPath(); ctx.moveTo( this.particleA.position.x, this.particleA.position.y ); ctx.quadraticCurveTo( this.particleB.position.x, this.particleB.position.y, this.particleC.position.x, this.particleC.position.y ); ctx.stroke(); ctx.closePath(); // reset line props ctx.lineCap = 'butt'; ctx.lineWidth = 1; }; // -------------------------- -------------------------- // var canvas = document.querySelector('canvas'); var ctx = canvas.getContext('2d'); var w = canvas.width; var h = canvas.height; // -------------------------- -------------------------- // // -------------------------- -------------------------- // var friction = 0.75; var gravity = new Vector( 0, 0.4 ); var movementStrength = 0.2; var springStrength = 0.5; var follicles = []; var pins = []; var v = new Vector( 112, 110 ); var follicle1 = new Follicle({ x: v.x, y: v.y, segmentLength: 54, angle: -1.75, curl: 1.17, friction: friction, gravity: gravity, springStrength: springStrength, movementStrength: movementStrength }); follicles.push( follicle1 ); v = new Vector( 140, 100 ); var follicle2 = new Follicle({ x: v.x, y: v.y, segmentLength: 63, angle: -1.33, curl: 1.15, friction: friction, gravity: gravity, springStrength: springStrength, movementStrength: movementStrength }); follicles.push( follicle2 ); v = new Vector( 165, 105 ); var follicle3 = new Follicle({ x: v.x, y: v.y, segmentLength: 54, angle: -1.05, curl: 1.15, friction: friction, gravity: gravity, springStrength: springStrength, movementStrength: movementStrength }); follicles.push( follicle3 ); v = new Vector( 178, 113 ); var follicle4 = new Follicle({ x: v.x, y: v.y, segmentLength: 52, angle: -0.63, curl: 1.15, friction: friction, gravity: gravity, springStrength: springStrength, movementStrength: movementStrength }); follicles.push( follicle4 ); v = new Vector( 185, 130 ); var follicle5 = new Follicle({ x: v.x, y: v.y, segmentLength: 46, angle: -0.29, curl: 1.15, friction: friction, gravity: gravity, springStrength: springStrength, movementStrength: movementStrength }); follicles.push( follicle5 ); v = new Vector( 180, 152 ); var follicle6 = new Follicle({ x: v.x, y: v.y, segmentLength: 40, angle: 0.05, curl: 1.15, friction: friction, gravity: gravity, springStrength: springStrength, movementStrength: movementStrength }); follicles.push( follicle6 ); v = new Vector( 160, 166 ); var follicle7 = new Follicle({ x: v.x, y: v.y, segmentLength: 30, angle: 0.45, curl: 0.8, friction: friction, gravity: gravity, springStrength: springStrength, movementStrength: movementStrength }); follicles.push( follicle7 ); // middle bottom v = new Vector( 145, 166 ); var follicle8 = new Follicle({ x: v.x, y: v.y, segmentLength: 26, angle: Math.PI / 2, curl: 0, friction: friction, gravity: gravity, springStrength: springStrength, movementStrength: movementStrength }); follicles.push( follicle8 ); // compare to 7 v = new Vector( 130, 166 ); var follicle9 = new Follicle({ x: v.x, y: v.y, segmentLength: 30, angle: 2.7, curl: -0.8, friction: friction, gravity: gravity, springStrength: springStrength, movementStrength: movementStrength }); follicles.push( follicle9 ); // compare to 6 v = new Vector( 118, 152 ); var follicle10 = new Follicle({ x: v.x, y: v.y, segmentLength: 46, angle: 3.20, curl: -1.15, friction: friction, gravity: gravity, springStrength: springStrength, movementStrength: movementStrength }); follicles.push( follicle10 ); // compare to 5 v = new Vector( 105, 130 ); var follicle11 = new Follicle({ x: v.x, y: v.y, segmentLength: 46, angle: -2.8, curl: -1.15, friction: friction, gravity: gravity, springStrength: springStrength, movementStrength: movementStrength }); follicles.push( follicle11 ); // compare to 4 v = new Vector( 120, 105 ); var follicle12 = new Follicle({ x: v.x, y: v.y, segmentLength: 52, angle: -2.5, curl: -1.15, friction: friction, gravity: gravity, springStrength: springStrength, movementStrength: movementStrength }); follicles.push( follicle12 ); // -------------------------- -------------------------- // var ribbon0 = new Ribbon({ controlPoint: new Vector( 130, 180 ), sections: 30, width: 40, sectionLength: 8, friction: 0.95, gravity: new Vector( 0, 0.2 ), chainLinkShiftEase: 0.9 }); var ribbon1 = new Ribbon({ controlPoint: new Vector( 130, 180 ), sections: 30, width: 40, sectionLength: 8, friction: 0.9, gravity: new Vector( 0, 0.25 ), chainLinkShiftEase: 0.9 }); // -------------------------- -------------------------- // var headImg = new Image(); var isHeadImgLoaded; headImg.onload = function() { isHeadImgLoaded = true; }; headImg.src = 'http://i.imgur.com/WtOekXc.png'; // -------------------------- -------------------------- // var origin = new Vector( 300, 300 ); var torso = new Vector(); var previousTorso = new Vector(); var coccyx = new Vector(); var head = new Vector(); var leftShoulder, leftElbow, leftWrist, leftHip, leftKnee, leftAnkle, leftToe; var rightShoulder, rightElbow, rightWrist, rightHip, rightKnee, rightAnkle, rightToe; var leftThigh, rightThigh; var leftShoulderOffset = new Vector( -40, 0 ); var rightShoulderOffset = new Vector( 30, 0 ); var shoulderAmplitude = 10; var armLength = 35; var leftHipOffset = new Vector( -40, 48 ); var rightHipOffset = new Vector( 10, 48 ); var hipAmplitude = 10; var legLength = 30; var footLength = 30; // -------------------------- -------------------------- // var cycleTheta = 0; var cycleSpeed = 0.07; var PI = Math.PI; var TAU = PI * 2; var breeze = new Vector( -0.5, 0 ); function update() { previousTorso.set( torso ); updateCycle(); var movement = previousTorso.x === 0 ? new Vector() : Vector.subtract( torso, previousTorso ); ribbon0.controlPoint.add( movement ); ribbon1.controlPoint.add( movement ); ribbon0.addBreeze( breeze ); ribbon1.addBreeze( breeze ); ribbon0.update(); ribbon1.update(); var i, len; for ( i=0, len = follicles.length; i < len; i++ ) { follicles[i].move( movement ); follicles[i].update(); } for ( i=0, len = pins.length; i < len; i++ ) { pins[i].update(); } for ( i=0, len = follicles.length; i < len; i++ ) { follicles[i].stick0.update(); follicles[i].stick1.update(); } } function updateCycle() { cycleTheta += cycleSpeed; var sin = Math.sin( cycleTheta ); torso.set( origin ); var lift = Math.cos( cycleTheta - 1 ); torso.y -= Math.abs( lift ) * 40; // torso.y -= Math.max( 0, Math.cos( cycleTheta * 2 - 2 ) ) * 20; coccyx.set( torso ); coccyx.y += leftHipOffset.y; head.set( torso ); head.y -= 30; // shoulder var quadFactor = 1.5; leftShoulder = Vector.add( torso, leftShoulderOffset ); var quadSine = sin > 0 ? quadWave( sin, quadFactor ) : sin; // var normTheta = normalizeAngle( cycleTheta ); // var quadSine = Math.floor( cycleTheta / ( TAU/4) ) % 2 ? quadWave( sin, squareFactor ) : sin; leftShoulder.x += quadSine * shoulderAmplitude; // elbow var leftElbowAngle = -quadSine * 1.0 + 1.7; leftElbow = Vector.addDistance( leftShoulder, armLength, leftElbowAngle ); // wrist var leftWristAngle = leftElbowAngle - quadSine * 0.4 - PI / 2; leftWrist = Vector.addDistance( leftElbow, armLength, leftWristAngle ); // hip leftHip = Vector.add( torso, leftHipOffset ); leftHip.x += -quadSine * hipAmplitude; // knee var leftKneeAngle = quadSine * 0.9 + 1.5; leftKnee = Vector.addDistance( leftHip, legLength, leftKneeAngle ); leftThigh = Vector.addDistance( leftHip, legLength/2, leftKneeAngle ); // ankle var ankleTheta = cycleTheta - TAU/8; var normAnkleTheta = normalizeAngle( ankleTheta ); var ankleAngle = Math.max( 0, Math.sin( normAnkleTheta * 2/3 ) ) * 2 - 1; var leftAnkleAngle = ( ankleAngle + 1 ) * 0.75; leftAnkleAngle += leftKneeAngle; leftAnkle = Vector.addDistance( leftKnee, legLength, leftAnkleAngle ); leftToe = Vector.addDistance( leftAnkle, footLength, leftAnkleAngle - TAU/4 ); // right // shoulder quadSine = sin < 0 ? quadWave( sin, quadFactor ) : sin; rightShoulder = Vector.add( torso, rightShoulderOffset ); rightShoulder.x += quadSine * shoulderAmplitude * -1; // elbow var rightElbowAngle = quadSine * 1.0 + 1.7; rightElbow = Vector.addDistance( rightShoulder, armLength, rightElbowAngle ); // wrist var rightWristAngle = rightElbowAngle + quadSine * 0.4 - PI / 2; rightWrist = Vector.addDistance( rightElbow, armLength, rightWristAngle ); // hip rightHip = Vector.add( torso, rightHipOffset ); rightHip.x += quadSine * hipAmplitude; // knee var rightKneeAngle = -quadSine * 0.9 + 1.5; rightKnee = Vector.addDistance( rightHip, legLength, rightKneeAngle ); rightThigh = Vector.addDistance( rightHip, legLength/2, rightKneeAngle ); // ankle ankleTheta = cycleTheta - TAU/8 + TAU/2; normAnkleTheta = normalizeAngle( ankleTheta ); ankleAngle = Math.max( 0, Math.sin( normAnkleTheta * 2/3 ) ) * 2 - 1; var rightAnkleAngle = ( ankleAngle + 1 ) * 0.75; rightAnkleAngle += rightKneeAngle; rightAnkle = Vector.addDistance( rightKnee, legLength, rightAnkleAngle ); rightToe = Vector.addDistance( rightAnkle, footLength, rightAnkleAngle - TAU/4 ); } var scale = 1; function render() { ctx.clearRect( 0, 0, w, h ); ctx.save(); ctx.scale( scale, scale ); ctx.save(); ctx.translate( 150, 50 ); ribbon0.render( ctx ); ribbon1.render( ctx ); for ( var i=0, len = follicles.length; i < len; i++ ) { follicles[i].render( ctx ); } ctx.restore(); renderIllo(); ctx.restore(); // renderSkeleton(); } var brownSkin = '#A74'; var black = '#333'; var magenta = '#B1B'; function renderIllo() { // right arm renderIlloArm( rightShoulder, rightElbow, rightWrist, true ); // right leg renderIlloLeg( rightHip, rightKnee, rightAnkle, rightThigh, rightToe, black ); // torso bottom ctx.fillStyle = black; ctx.beginPath(); ctx.arc( leftHip.x + 15, leftHip.y - 15, 42, TAU/4, -TAU/4 ); ctx.fill(); ctx.closePath(); ctx.beginPath(); ctx.arc( rightHip.x - 15, rightHip.y - 15, 42, -TAU/4, TAU/4 ); ctx.fill(); ctx.closePath(); ctx.fillRect( leftHip.x + 14.5, torso.y - 9, ( rightHip.x - 14.5) - (leftHip.x + 14.5), 84 ); // torso top ctx.fillStyle = black; fillCircle( Vector.add( torso, { x: -33, y: 0 } ), 25 ); fillCircle( Vector.add( torso, { x: 18, y: 0 } ), 25 ); // fillCircle( Vector.add( rightShoulder, { x: -15, y: 0 } ), 25 ); ctx.fillRect( torso.x - 33, torso.y - 25, 56, 50 ); // head if ( isHeadImgLoaded ) { ctx.drawImage( headImg, torso.x - 70, torso.y - 145 ); } // left leg renderIlloLeg( leftHip, leftKnee, leftAnkle, leftThigh, leftToe, magenta ); // left arm renderIlloArm( leftShoulder, leftElbow, leftWrist ); } function renderIlloArm( shoulder, elbow, wrist, hasBand ) { ctx.strokeStyle = brownSkin; ctx.lineWidth = 45; ctx.lineCap = 'round'; ctx.lineJoin = 'round'; ctx.beginPath(); ctx.moveTo( shoulder.x, shoulder.y ); ctx.lineTo( elbow.x, elbow.y ); ctx.lineTo( wrist.x, wrist.y ); ctx.stroke(); ctx.closePath(); if ( hasBand ) { ctx.strokeStyle = magenta; ctx.beginPath(); ctx.moveTo( elbow.x, elbow.y ); ctx.lineTo( wrist.x, wrist.y ); ctx.stroke(); ctx.closePath(); } // ctx.fillStyle = !hasBand ? magenta : brownSkin; ctx.fillStyle = brownSkin; fillCircle( wrist, 28 ); } function fillCircle( v, radius ) { ctx.beginPath(); ctx.arc( v.x, v.y, radius, 0, TAU ); ctx.fill(); ctx.closePath(); } function renderIlloLeg( hip, knee, ankle, thigh, toe, footColor ) { ctx.lineWidth = 45; ctx.lineCap = 'round'; ctx.lineJoin = 'round'; ctx.strokeStyle = black; ctx.beginPath(); ctx.moveTo( hip.x, hip.y ); ctx.lineTo( knee.x, knee.y ); ctx.lineTo( ankle.x, ankle.y ); ctx.stroke(); ctx.closePath(); // foot ctx.lineCap = 'round'; ctx.lineWidth = 50; ctx.strokeStyle = magenta; ctx.beginPath(); ctx.moveTo( ankle.x, ankle.y ); ctx.lineTo( toe.x, toe.y ); ctx.stroke(); ctx.closePath(); } function dot( ctx, v ) { ctx.beginPath(); ctx.arc( v.x, v.y, 6, 0, Math.PI * 2 ); ctx.fill(); ctx.closePath(); } // i: sin or cos // b: square factor function quadWave( i, b ) { return Math.sqrt( ( 1 + b * b ) / ( 1 + b * b * i * i ) ) * i; } var isAnimating = false; function animate() { update(); render(); requestAnimationFrame( animate ); } function start() { isAnimating = true; } // -------------------------- -------------------------- // animate(); var speedRange = document.querySelector('.speed-range') speedRange.onchange = function() { cycleSpeed = parseFloat( speedRange.value ); };
CSS
body { text-align: center; } canvas { display: block; margin: 0 auto; }
HTML
Speed
Join Effecthub.com
Working with Global Gaming Artists and Developers!
Login
Sign Up
Or Login with Your Email Address:
Email
Password
Remember
Or Sign Up with Your Email Address:
Your Email
This field must contain a valid email
Set Password
Password should be at least 1 character
Stay informed via email