let samplesPerFrame = 5; let numFrames = 50; let shutterAngle = 1.5; const canvas = document.querySelector( 'canvas' ); let w = canvas.width = 400; let h = canvas.height = 400; const ctx = context = canvas.getContext( '2d' ); const gif = new GIF( { workers: 2, quality: 10, width: w, height: h } ); gif.on( 'finished', blob => { window.open( URL.createObjectURL( blob ) ); } ); const mouse = { x: 0, y: 0, pressed: false }; canvas.addEventListener( 'mousemove', e => { mouse.x = e.clientX; mouse.y = e.clientY; } ); canvas.addEventListener( 'mousedown', e => mouse.pressed = true ); canvas.addEventListener( 'mouseup', e => mouse.pressed = false ); canvas.addEventListener( 'click', e => console.log( mouse ) ); let recording = false; let frame = 0; document.body.addEventListener( 'keydown', e => { recording = true; frame = 0; } ); ( function anim( t ){ requestAnimationFrame( anim ); if( ! recording ){ if( mouse.pressed ){ draw( ( mouse.x + 1 ) / w ); } else{ draw( t / 1000 ); } } else { let frameData = new ImageData( w, h ); for( let sample = 0; sample < samplesPerFrame; sample ++ ) { let t = ( frame + sample * shutterAngle / samplesPerFrame ) / numFrames; draw( t ); let sampleData = ctx.getImageData( 0, 0, w, h ); for( let i = 0; i < sampleData.data.length; i ++ ) { frameData.data[ i ] += sampleData.data[ i ] / samplesPerFrame; } } ctx.putImageData( frameData, 0, 0 ); gif.addFrame(ctx, {copy: false}); // let img = document.createElement( 'img' ); // img.src = canvas.toDataURL(); // document.body.appendChild( img ); frame ++; if( frame === numFrames ){ recording = false; gif.render(); } } } )(); function draw( t ){ ctx.fillStyle = '#000'; ctx.fillRect( 0, 0, w, h ); ctx.fillStyle = 'white'; ctx.beginPath(); ctx.arc(w / 2 + 100 * Math.cos( Math.PI * 2 * t ) , h / 2 + 100 * Math.sin( Math.PI * 2 * t ), 10, 0, 2 * Math.PI ); ctx.fill(); }