canvas曲线运动阳光下的泡沫效果泡沫曲线运动矩阵数字雨烟花效果方块旋转

本文主要是介绍canvas曲线运动阳光下的泡沫效果泡沫曲线运动矩阵数字雨烟花效果方块旋转,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

文章目录

    • canvas曲线运动&阳光下的泡沫效果&泡沫曲线运动&矩阵数字雨&烟花效果&方块旋转
    • 曲线运动
      • 勾股定理
      • 三角函数
      • 什么是弧度
      • 角度转弧度
      • 弧度转角度
      • 三角函数图像
        • 与canvas结合
      • 曲线运动案例代码
      • 阳光下的泡沫效果
        • 结合canvas实现气泡效果
        • 效果图
        • 完整代码
      • 泡沫曲线运动
        • 效果图
        • 完整代码
      • 矩阵数字雨
        • 效果图
        • 完整代码
      • 环形方块旋转
        • 效果图
        • 完整代码
      • canvas烟花效果
        • 效果图
        • 完整代码
      • canvas制作马赛克&飞鸟动画&小球拖拽动画
      • canvas实现移动端刮刮卡
      • 带你使用canvas实现时钟
      • 后记

canvas曲线运动&阳光下的泡沫效果&泡沫曲线运动&矩阵数字雨&烟花效果&方块旋转

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

曲线运动

曲线运动

勾股定理

a * a + b*b =c*c

三角函数

	正弦 : sin余弦 : cos正切 : tan余切 : cot正弦定理a/sinA = b/sinB =c/sinC = 2r(r为外接圆半径)余弦定理cosA = b*b + c*c - a*a / 2bccosB = c*c + a*a - b*b / 2cacosC = a*a + b*b - c*c / 2ab

什么是弧度

	一个角度到底代表多少弧度:这个角度所包含的外接圆的弧长/外接圆的半径360 角度 = 2*PI*r/r 弧度(360角度  = 2*PI 弧度)   		===> (单位换算)1角度 = PI/180  弧度 1弧度 = 180/PI 角度

角度转弧度

弧度值 = 角度值*PI/180

弧度转角度

角度值 = 弧度值*180/PI

三角函数图像

与canvas结合

人眼能接收的最好频率为一秒钟60次,这样的体验是比较好的

在这里插入图片描述

曲线运动案例代码

<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title></title><style type="text/css">*{margin: 0;padding: 0;}#test{position: absolute;left: 200px;top: 300px;width: 10px;height: 10px;background: black;}.box{position: absolute;border: 1px solid;}</style></head><body><div id="test"></div></body><script type="text/javascript">window.onload=function(){var testNode = document.querySelector("#test");var startX = testNode.offsetLeft;var startY = testNode.offsetTop;//角度var deg =0;var step = 100;setInterval(function(){deg++;testNode.style.left = startX + (deg*Math.PI/180)*step/2 +'px';testNode.style.top = startY + Math.tan( deg*Math.PI/180 )*step*2+"px";var boxNode = document.createElement("div");boxNode.classList.add("box");boxNode.style.left=testNode.offsetLeft+"px";boxNode.style.top=testNode.offsetTop+"px";document.body.appendChild(boxNode);},1000/60)}</script>
</html>

阳光下的泡沫效果

结合canvas实现气泡效果

第二个循环定时器:
维护一个数组(随机圆的信息)
圆心
半径
rgba值
初始位置
波峰 波谷的值
度数

第一个循环定时器:
在canvas上实现动画
第一个for循环(canvas上需要动画的元素不止一个)
将随机圆数组中需要动画的参数拿出来进行平滑的累加
第一个for循环(canvas上需要动画的元素不止一个)
使用canvas api进行绘制

效果图

在这里插入图片描述

完整代码
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title></title><style type="text/css">*{margin: 0;padding: 0;}body{background: pink;}canvas{position: absolute;left: 0;right: 0;top: 0;bottom: 0;margin: auto;background: white;}</style></head><body><canvas width="400" height="400"></canvas></body><script type="text/javascript">window.onload=function(){var oc = document.querySelector("canvas");if(oc.getContext){var ctx = oc.getContext("2d");var arr=[];//将数组中的圆绘制到画布上setInterval(function(){ctx.clearRect(0,0,oc.width,oc.height);//动画for(var i=0;i<arr.length;i++){if(arr[i].alp<=0){arr.splice(i,1);}arr[i].r++;arr[i].alp-=0.01;}//绘制for(var i=0;i<arr.length;i++){ctx.save();ctx.fillStyle="rgba("+arr[i].red+","+arr[i].green+","+arr[i].blue+","+arr[i].alp+")";ctx.beginPath();ctx.arc(arr[i].x,arr[i].y,arr[i].r,0,2*Math.PI);ctx.fill();ctx.restore();}},1000/60)//往arr中注入随机圆的信息setInterval(function(){var x = Math.random()*oc.width;var y = Math.random()*oc.height;var r =10;var red =   Math.round(Math.random()*255);var green = Math.round(Math.random()*255);var blue =  Math.round(Math.random()*255);var alp = 1;arr.push({x:x,y:y,r:r,red:red,green:green,blue:blue,alp:alp})},50)}}</script>
</html>

泡沫曲线运动

效果图

在这里插入图片描述

完整代码
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title></title><style type="text/css">*{margin: 0;padding: 0;}body{background: pink;}canvas{position: absolute;left: 0;right: 0;top: 0;bottom: 0;margin: auto;background: white;}</style></head><body><canvas width="150" height="400"></canvas></body><script type="text/javascript">window.onload=function(){var oc = document.querySelector("canvas");if(oc.getContext){var ctx = oc.getContext("2d");var arr=[];//将数组中的圆绘制到画布上setInterval(function(){console.log(arr)ctx.clearRect(0,0,oc.width,oc.height);//动画for(var i=0;i<arr.length;i++){arr[i].deg+=5;arr[i].x = arr[i].startX +  Math.sin( arr[i].deg*Math.PI/180 )*arr[i].step*2;arr[i].y = arr[i].startY - (arr[i].deg*Math.PI/180)*arr[i].step ;if(arr[i].y <=50){arr.splice(i,1)}}//绘制for(var i=0;i<arr.length;i++){ctx.save();ctx.fillStyle="rgba("+arr[i].red+","+arr[i].green+","+arr[i].blue+","+arr[i].alp+")";ctx.beginPath();ctx.arc(arr[i].x,arr[i].y,arr[i].r,0,2*Math.PI);ctx.fill();ctx.restore();}},1000/60)//往arr中注入随机圆的信息setInterval(function(){var r =Math.random()*6+2;var x = Math.random()*oc.width;var y = oc.height - r;var red =   Math.round(Math.random()*255);var green = Math.round(Math.random()*255);var blue =  Math.round(Math.random()*255);var alp = 1;var deg =0;var startX = x;var startY = y;//曲线的运动形式var step =Math.random()*20+10;arr.push({x:x,y:y,r:r,red:red,green:green,blue:blue,alp:alp,deg:deg,startX:startX,startY:startY,step:step})},50)}}</script>
</html>

矩阵数字雨

效果图

在这里插入图片描述

完整代码

CSS

@charset "utf-8";
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent}
body{line-height:1}#iframe-wrap{height:100%;overflow:visible;position:relative;top:54px;z-index:50}
.tablet-width{height:960px!important;margin:0 auto;padding:175px 100px 115px 100px;width:785px;margin-top:40px;background:url(../i/bg-mob.png) no-repeat 0 0}
.tablet-width iframe{height:960px!important}
.mobile-width{height:703px!important;margin:0 auto;padding:165px 115px 100px 100px;width:1041px;margin-top:40px;background:url(../i/bg-mob.png) no-repeat 0 -1249px}
.mobile-width iframe{height:704px!important}
.mobile-width-2{height:417px!important;margin:0 auto;padding:125px 25px 159px 25px;width:337px;margin-top:40px;background:url(../i/bg-mob.png) no-repeat 0 -2217px}
.mobile-width-2 iframe{height:416px!important}
.mobile-width-3{height:256px!important;margin:0 auto;padding:45px 115px 69px 105px;width:497px;margin-top:40px;background:url(../i/bg-mob.png) no-repeat -387px -2217px}
.mobile-width-3 iframe{height:256px!important}
#by{overflow-y:hidden}

HTML&JavaScript

<!DOCTYPE html>
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0"><title>矩阵数字雨canvas动效</title><link href="css/demo.css" rel="stylesheet" media="all" /><!--[if IE]><style type="text/css">li.remove_frame a {padding-top: 5px;background-position: 0px -3px;}</style><![endif]--><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript">$(document).ready(function () {function fixHeight() {var headerHeight = $("#switcher").height();$("#iframe").attr("height", $(window).height()-54+ "px");}$(window).resize(function () {fixHeight();}).resize();$('.icon-monitor').addClass('active');$(".icon-mobile-3").click(function () {$("#by").css("overflow-y", "auto");$('#iframe-wrap').removeClass().addClass('mobile-width-3');$('.icon-tablet,.icon-mobile-1,.icon-monitor,.icon-mobile-2,.icon-mobile-3').removeClass('active');$(this).addClass('active');return false;});$(".icon-mobile-2").click(function () {$("#by").css("overflow-y", "auto");$('#iframe-wrap').removeClass().addClass('mobile-width-2');$('.icon-tablet,.icon-mobile-1,.icon-monitor,.icon-mobile-2,.icon-mobile-3').removeClass('active');$(this).addClass('active');return false;});$(".icon-mobile-1").click(function () {$("#by").css("overflow-y", "auto");$('#iframe-wrap').removeClass().addClass('mobile-width');$('.icon-tablet,.icon-mobile,.icon-monitor,.icon-mobile-2,.icon-mobile-3').removeClass('active');$(this).addClass('active');return false;});$(".icon-tablet").click(function () {$("#by").css("overflow-y", "auto");$('#iframe-wrap').removeClass().addClass('tablet-width');$('.icon-tablet,.icon-mobile-1,.icon-monitor,.icon-mobile-2,.icon-mobile-3').removeClass('active');$(this).addClass('active');return false;});$(".icon-monitor").click(function () {$("#by").css("overflow-y", "hidden");$('#iframe-wrap').removeClass().addClass('full-width');$('.icon-tablet,.icon-mobile-1,.icon-monitor,.icon-mobile-2,.icon-mobile-3').removeClass('active');$(this).addClass('active');return false;});});</script><script type="text/javascript">function Responsive($a) {if ($a == true) $("#Device").css("opacity", "100");if ($a == false) $("#Device").css("opacity", "0");$('#iframe-wrap').removeClass().addClass('full-width');$('.icon-tablet,.icon-mobile-1,.icon-monitor,.icon-mobile-2,.icon-mobile-3').removeClass('active');$(this).addClass('active');return false;};</script></head>
<body id="by"><div id="iframe-wrap"><iframe id="iframe" src="https://www.17sucai.com/preview/1424582/2020-03-29/jz/index.html" frameborder="0"  width="100%"></iframe>
</div></body>
</html>

环形方块旋转

效果图

在这里插入图片描述

完整代码

HTML

<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>HTML5 Canvas 3D环形方块翻转动画DEMO演示</title><link rel="stylesheet" href="css/style.css"></head>
<body><canvas class="zdog-canvas" width="480" height="480"></canvas><script src='js/zdog.dist.js'></script>
<script src="js/script.js"></script>
<div style="text-align:center;clear:both;">
<script src="/gg_bd_ad_720x90.js" type="text/javascript"></script>
<script src="/follow.js" type="text/javascript"></script>
</div></body>
</html>

JavaScript

https://zzz.dog/
在这里插入图片描述

<script src='js/zdog.dist.js'></script>

/*!* Zdog v1.1.0* Round, flat, designer-friendly pseudo-3D engine* Licensed MIT* https://zzz.dog* Copyright 2019 Metafizzy*//*** Boilerplate & utils*/( function( root, factory ) {// module definitionif ( typeof module == 'object' && module.exports ) {// CommonJSmodule.exports = factory();} else {// browser globalroot.Zdog = factory();}
}( this, function factory() {var Zdog = {};Zdog.TAU = Math.PI * 2;Zdog.extend = function( a, b ) {for ( var prop in b ) {a[ prop ] = b[ prop ];}return a;
};Zdog.lerp = function( a, b, alpha ) {return ( b - a ) * alpha + a;
};Zdog.modulo = function( num, div ) {return ( ( num % div ) + div ) % div;
};var powerMultipliers = {2: function( a ) {return a * a;},3: function( a ) {return a * a * a;},4: function( a ) {return a * a * a * a;},5: function( a ) {return a * a * a * a * a;},
};Zdog.easeInOut = function( alpha, power ) {if ( power == 1 ) {return alpha;}alpha = Math.max( 0, Math.min( 1, alpha ) );var isFirstHalf = alpha < 0.5;var slope = isFirstHalf ? alpha : 1 - alpha;slope /= 0.5;// make easing steeper with more multiplesvar powerMultiplier = powerMultipliers[ power ] || powerMultipliers[2];var curve = powerMultiplier( slope );curve /= 2;return isFirstHalf ? curve : 1 - curve;
};return Zdog;}));
/*** CanvasRenderer*/( function( root, factory ) {// module definitionif ( typeof module == 'object' && module.exports ) {// CommonJSmodule.exports = factory();} else {// browser globalroot.Zdog.CanvasRenderer = factory();}
}( this, function factory() {var CanvasRenderer = { isCanvas: true };CanvasRenderer.begin = function( ctx ) {ctx.beginPath();
};CanvasRenderer.move = function( ctx, elem, point ) {ctx.moveTo( point.x, point.y );
};CanvasRenderer.line = function( ctx, elem, point ) {ctx.lineTo( point.x, point.y );
};CanvasRenderer.bezier = function( ctx, elem, cp0, cp1, end ) {ctx.bezierCurveTo( cp0.x, cp0.y, cp1.x, cp1.y, end.x, end.y );
};CanvasRenderer.closePath = function( ctx ) {ctx.closePath();
};CanvasRenderer.setPath = function() {};CanvasRenderer.renderPath = function( ctx, elem, pathCommands, isClosed ) {this.begin( ctx, elem );pathCommands.forEach( function( command ) {command.render( ctx, elem, CanvasRenderer );});if ( isClosed ) {this.closePath( ctx, elem );}
};CanvasRenderer.stroke = function( ctx, elem, isStroke, color, lineWidth ) {if ( !isStroke ) {return;}ctx.strokeStyle = color;ctx.lineWidth = lineWidth;ctx.stroke();
};CanvasRenderer.fill = function( ctx, elem, isFill, color ) {if ( !isFill ) {return;}ctx.fillStyle = color;ctx.fill();
};CanvasRenderer.end = function() {};return CanvasRenderer;}));
/*** SvgRenderer*/( function( root, factory ) {// module definitionif ( typeof module == 'object' && module.exports ) {// CommonJSmodule.exports = factory();} else {// browser globalroot.Zdog.SvgRenderer = factory();}
}( this, function factory() {var SvgRenderer = { isSvg: true };// round path coordinates to 3 decimals
var round = SvgRenderer.round = function( num ) {return Math.round( num * 1000 ) / 1000;
};function getPointString( point ) {return round( point.x ) + ',' + round( point.y ) + ' ';
}SvgRenderer.begin = function() {};SvgRenderer.move = function( svg, elem, point ) {return 'M' + getPointString( point );
};SvgRenderer.line = function( svg, elem, point ) {return 'L' + getPointString( point );
};SvgRenderer.bezier = function( svg, elem, cp0, cp1, end ) {return 'C' + getPointString( cp0 ) + getPointString( cp1 ) +getPointString( end );
};SvgRenderer.closePath = function(/* elem */) {return 'Z';
};SvgRenderer.setPath = function( svg, elem, pathValue ) {elem.setAttribute( 'd', pathValue );
};SvgRenderer.renderPath = function( svg, elem, pathCommands, isClosed ) {var pathValue = '';pathCommands.forEach( function( command ) {pathValue += command.render( svg, elem, SvgRenderer );});if ( isClosed ) {pathValue += this.closePath( svg, elem );}this.setPath( svg, elem, pathValue );
};SvgRenderer.stroke = function( svg, elem, isStroke, color, lineWidth ) {if ( !isStroke ) {return;}elem.setAttribute( 'stroke', color );elem.setAttribute( 'stroke-width', lineWidth );
};SvgRenderer.fill = function( svg, elem, isFill, color ) {var fillColor = isFill ? color : 'none';elem.setAttribute( 'fill', fillColor );
};SvgRenderer.end = function( svg, elem ) {svg.appendChild( elem );
};return SvgRenderer;}));
/*** Vector*/( function( root, factory ) {// module definitionif ( typeof module == 'object' && module.exports ) {// CommonJSmodule.exports = factory( require('./boilerplate') );} else {// browser globalvar Zdog = root.Zdog;Zdog.Vector = factory( Zdog );}}( this, function factory( utils ) {function Vector( position ) {this.set( position );
}var TAU = utils.TAU;// 'pos' = 'position'
Vector.prototype.set = function( pos ) {this.x = pos && pos.x || 0;this.y = pos && pos.y || 0;this.z = pos && pos.z || 0;return this;
};// set coordinates without sanitizing
// vec.write({ y: 2 }) only sets y coord
Vector.prototype.write = function( pos ) {if ( !pos ) {return this;}this.x = pos.x != undefined ? pos.x : this.x;this.y = pos.y != undefined ? pos.y : this.y;this.z = pos.z != undefined ? pos.z : this.z;return this;
};Vector.prototype.rotate = function( rotation ) {if ( !rotation ) {return;}this.rotateZ( rotation.z );this.rotateY( rotation.y );this.rotateX( rotation.x );return this;
};Vector.prototype.rotateZ = function( angle ) {rotateProperty( this, angle, 'x', 'y' );
};Vector.prototype.rotateX = function( angle ) {rotateProperty( this, angle, 'y', 'z' );
};Vector.prototype.rotateY = function( angle ) {rotateProperty( this, angle, 'x', 'z' );
};function rotateProperty( vec, angle, propA, propB ) {if ( !angle || angle % TAU === 0 ) {return;}var cos = Math.cos( angle );var sin = Math.sin( angle );var a = vec[ propA ];var b = vec[ propB ];vec[ propA ] = a*cos - b*sin;vec[ propB ] = b*cos + a*sin;
}Vector.prototype.isSame = function( pos ) {if ( !pos ) {return false;}return this.x === pos.x && this.y === pos.y && this.z === pos.z;
};Vector.prototype.add = function( pos ) {if ( !pos ) {return this;}this.x += pos.x || 0;this.y += pos.y || 0;this.z += pos.z || 0;return this;
};Vector.prototype.subtract = function( pos ) {if ( !pos ) {return this;}this.x -= pos.x || 0;this.y -= pos.y || 0;this.z -= pos.z || 0;return this;
};Vector.prototype.multiply = function( pos ) {if ( pos == undefined ) {return this;}// multiple all values by same numberif ( typeof pos == 'number' ) {this.x *= pos;this.y *= pos;this.z *= pos;} else {// multiply objectthis.x *= pos.x != undefined ? pos.x : 1;this.y *= pos.y != undefined ? pos.y : 1;this.z *= pos.z != undefined ? pos.z : 1;}return this;
};Vector.prototype.transform = function( translation, rotation, scale ) {this.multiply( scale );this.rotate( rotation );this.add( translation );return this;
};Vector.prototype.lerp = function( pos, alpha ) {this.x = utils.lerp( this.x, pos.x || 0, alpha );this.y = utils.lerp( this.y, pos.y || 0, alpha );this.z = utils.lerp( this.z, pos.z || 0, alpha );return this;
};Vector.prototype.magnitude = function() {var sum = this.x*this.x + this.y*this.y + this.z*this.z;return getMagnitudeSqrt( sum );
};function getMagnitudeSqrt( sum ) {// PERF: check if sum ~= 1 and skip sqrtif ( Math.abs( sum - 1 ) < 0.00000001 ) {return 1;}return Math.sqrt( sum );
}Vector.prototype.magnitude2d = function() {var sum = this.x*this.x + this.y*this.y;return getMagnitudeSqrt( sum );
};Vector.prototype.copy = function() {return new Vector( this );
};return Vector;}));
/*** Anchor*/( function( root, factory ) {// module definitionif ( typeof module == 'object' && module.exports ) {// CommonJSmodule.exports = factory( require('./boilerplate'), require('./vector'),require('./canvas-renderer'), require('./svg-renderer') );} else {// browser globalvar Zdog = root.Zdog;Zdog.Anchor = factory( Zdog, Zdog.Vector, Zdog.CanvasRenderer,Zdog.SvgRenderer );}
}( this, function factory( utils, Vector, CanvasRenderer, SvgRenderer ) {var TAU = utils.TAU;
var onePoint = { x: 1, y: 1, z: 1 };function Anchor( options ) {this.create( options || {} );
}Anchor.prototype.create = function( options ) {this.children = [];// set defaults & optionsutils.extend( this, this.constructor.defaults );this.setOptions( options );// transformthis.translate = new Vector( options.translate );this.rotate = new Vector( options.rotate );this.scale = new Vector( onePoint ).multiply( this.scale );// originthis.origin = new Vector();this.renderOrigin = new Vector();if ( this.addTo ) {this.addTo.addChild( this );}
};Anchor.defaults = {};Anchor.optionKeys = Object.keys( Anchor.defaults ).concat(['rotate','translate','scale','addTo',
]);Anchor.prototype.setOptions = function( options ) {var optionKeys = this.constructor.optionKeys;for ( var key in options ) {if ( optionKeys.indexOf( key ) != -1 ) {this[ key ] = options[ key ];}}
};Anchor.prototype.addChild = function( shape ) {if ( this.children.indexOf( shape ) != -1 ) {return;}shape.remove(); // remove previous parentshape.addTo = this; // keep parent referencethis.children.push( shape );
};Anchor.prototype.removeChild = function( shape ) {var index = this.children.indexOf( shape );if ( index != -1 ) {this.children.splice( index, 1 );}
};Anchor.prototype.remove = function() {if ( this.addTo ) {this.addTo.removeChild( this );}
};// ----- update ----- //Anchor.prototype.update = function() {// update selfthis.reset();// update childrenthis.children.forEach( function( child ) {child.update();});this.transform( this.translate, this.rotate, this.scale );
};Anchor.prototype.reset = function() {this.renderOrigin.set( this.origin );
};Anchor.prototype.transform = function( translation, rotation, scale ) {this.renderOrigin.transform( translation, rotation, scale );// transform childrenthis.children.forEach( function( child ) {child.transform( translation, rotation, scale );});
};Anchor.prototype.updateGraph = function() {this.update();this.updateFlatGraph();this.flatGraph.forEach( function( item ) {item.updateSortValue();});// z-sortthis.flatGraph.sort( Anchor.shapeSorter );
};Anchor.shapeSorter = function( a, b ) {return a.sortValue - b.sortValue;
};// custom getter to check for flatGraph before using it
Object.defineProperty( Anchor.prototype, 'flatGraph', {get: function() {if ( !this._flatGraph ) {this.updateFlatGraph();}return this._flatGraph;},set: function( graph ) {this._flatGraph = graph;},
});Anchor.prototype.updateFlatGraph = function() {this.flatGraph = this.getFlatGraph();
};// return Array of self & all child graph items
Anchor.prototype.getFlatGraph = function() {var flatGraph = [ this ];return this.addChildFlatGraph( flatGraph );
};Anchor.prototype.addChildFlatGraph = function( flatGraph ) {this.children.forEach( function( child ) {var childFlatGraph = child.getFlatGraph();Array.prototype.push.apply( flatGraph, childFlatGraph );});return flatGraph;
};Anchor.prototype.updateSortValue = function() {this.sortValue = this.renderOrigin.z;
};// ----- render ----- //Anchor.prototype.render = function() {};// TODO refactor out CanvasRenderer so its not a dependency within anchor.js
Anchor.prototype.renderGraphCanvas = function( ctx ) {if ( !ctx ) {throw new Error( 'ctx is ' + ctx + '. ' +'Canvas context required for render. Check .renderGraphCanvas( ctx ).' );}this.flatGraph.forEach( function( item ) {item.render( ctx, CanvasRenderer );});
};Anchor.prototype.renderGraphSvg = function( svg ) {if ( !svg ) {throw new Error( 'svg is ' + svg + '. ' +'SVG required for render. Check .renderGraphSvg( svg ).' );}this.flatGraph.forEach( function( item ) {item.render( svg, SvgRenderer );});
};// ----- misc ----- //Anchor.prototype.copy = function( options ) {// copy optionsvar itemOptions = {};var optionKeys = this.constructor.optionKeys;optionKeys.forEach( function( key ) {itemOptions[ key ] = this[ key ];}, this );// add set optionsutils.extend( itemOptions, options );var ItemClass = this.constructor;return new ItemClass( itemOptions );
};Anchor.prototype.copyGraph = function( options ) {var clone = this.copy( options );this.children.forEach( function( child ) {child.copyGraph({addTo: clone,});});return clone;
};Anchor.prototype.normalizeRotate = function() {this.rotate.x = utils.modulo( this.rotate.x, TAU );this.rotate.y = utils.modulo( this.rotate.y, TAU );this.rotate.z = utils.modulo( this.rotate.z, TAU );
};// ----- subclass ----- //function getSubclass( Super ) {return function( defaults ) {// create constructorfunction Item( options ) {this.create( options || {} );}Item.prototype = Object.create( Super.prototype );Item.prototype.constructor = Item;Item.defaults = utils.extend( {}, Super.defaults );utils.extend( Item.defaults, defaults );// create optionKeysItem.optionKeys = Super.optionKeys.slice(0);// add defaults keys to optionKeys, dedupeObject.keys( Item.defaults ).forEach( function( key ) {if ( !Item.optionKeys.indexOf( key ) != 1 ) {Item.optionKeys.push( key );}});Item.subclass = getSubclass( Item );return Item;};
}Anchor.subclass = getSubclass( Anchor );return Anchor;}));
/*** Dragger*/( function( root, factory ) {// module definitionif ( typeof module == 'object' && module.exports ) {// CommonJSmodule.exports = factory( root );} else {// browser globalroot.Zdog.Dragger = factory( root );}
}( this, function factory( window ) {// quick & dirty drag event stuff
// messes up if multiple pointers/touches// event support, default to mouse events
var downEvent = 'mousedown';
var moveEvent = 'mousemove';
var upEvent = 'mouseup';
if ( window.PointerEvent ) {// PointerEvent, ChromedownEvent = 'pointerdown';moveEvent = 'pointermove';upEvent = 'pointerup';
} else if ( 'ontouchstart' in window ) {// Touch Events, iOS SafaridownEvent = 'touchstart';moveEvent = 'touchmove';upEvent = 'touchend';
}function noop() {}function Dragger( options ) {this.create( options || {} );
}Dragger.prototype.create = function( options ) {this.onDragStart = options.onDragStart || noop;this.onDragMove = options.onDragMove || noop;this.onDragEnd = options.onDragEnd || noop;this.bindDrag( options.startElement );
};Dragger.prototype.bindDrag = function( element ) {element = this.getQueryElement( element );if ( !element ) {return;}// disable browser gestures #53element.style.touchAction = 'none';element.addEventListener( downEvent, this );
};Dragger.prototype.getQueryElement = function( element ) {if ( typeof element == 'string' ) {// with string, query selectorelement = document.querySelector( element );}return element;
};Dragger.prototype.handleEvent = function( event ) {var method = this[ 'on' + event.type ];if ( method ) {method.call( this, event );}
};Dragger.prototype.onmousedown =
Dragger.prototype.onpointerdown = function( event ) {this.dragStart( event, event );
};Dragger.prototype.ontouchstart = function( event ) {this.dragStart( event, event.changedTouches[0] );
};Dragger.prototype.dragStart = function( event, pointer ) {event.preventDefault();this.dragStartX = pointer.pageX;this.dragStartY = pointer.pageY;window.addEventListener( moveEvent, this );window.addEventListener( upEvent, this );this.onDragStart( pointer );
};Dragger.prototype.ontouchmove = function( event ) {// HACK, moved touch may not be firstthis.dragMove( event, event.changedTouches[0] );
};Dragger.prototype.onmousemove =
Dragger.prototype.onpointermove = function( event ) {this.dragMove( event, event );
};Dragger.prototype.dragMove = function( event, pointer ) {event.preventDefault();var moveX = pointer.pageX - this.dragStartX;var moveY = pointer.pageY - this.dragStartY;this.onDragMove( pointer, moveX, moveY );
};Dragger.prototype.onmouseup =
Dragger.prototype.onpointerup =
Dragger.prototype.ontouchend =
Dragger.prototype.dragEnd = function(/* event */) {window.removeEventListener( moveEvent, this );window.removeEventListener( upEvent, this );this.onDragEnd();
};return Dragger;}));
/*** Illustration*/( function( root, factory ) {// module definitionif ( typeof module == 'object' && module.exports ) {// CommonJSmodule.exports = factory( require('./boilerplate'), require('./anchor'),require('./dragger') );} else {// browser globalvar Zdog = root.Zdog;Zdog.Illustration = factory( Zdog, Zdog.Anchor, Zdog.Dragger );}
}( this, function factory( utils, Anchor, Dragger ) {function noop() {}
var TAU = utils.TAU;var Illustration = Anchor.subclass({element: undefined,centered: true,zoom: 1,dragRotate: false,resize: false,onPrerender: noop,onDragStart: noop,onDragMove: noop,onDragEnd: noop,onResize: noop,
});utils.extend( Illustration.prototype, Dragger.prototype );Illustration.prototype.create = function( options ) {Anchor.prototype.create.call( this, options );Dragger.prototype.create.call( this, options );this.setElement( this.element );this.setDragRotate( this.dragRotate );this.setResize( this.resize );
};Illustration.prototype.setElement = function( element ) {element = this.getQueryElement( element );if ( !element ) {throw new Error( 'Zdog.Illustration element required. Set to ' + element );}var nodeName = element.nodeName.toLowerCase();if ( nodeName == 'canvas' ) {this.setCanvas( element );} else if ( nodeName == 'svg' ) {this.setSvg( element );}
};Illustration.prototype.setSize = function( width, height ) {width = Math.round( width );height = Math.round( height );if ( this.isCanvas ) {this.setSizeCanvas( width, height );} else if ( this.isSvg ) {this.setSizeSvg( width, height );}
};Illustration.prototype.setResize = function( resize ) {this.resize = resize;// create resize event listenerif ( !this.resizeListener ) {this.resizeListener = this.onWindowResize.bind( this );}// add/remove event listenerif ( resize ) {window.addEventListener( 'resize', this.resizeListener );this.onWindowResize();} else {window.removeEventListener( 'resize', this.resizeListener );}
};// TODO debounce this?
Illustration.prototype.onWindowResize = function() {this.setMeasuredSize();this.onResize( this.width, this.height );
};Illustration.prototype.setMeasuredSize = function() {var width, height;var isFullscreen = this.resize == 'fullscreen';if ( isFullscreen ) {width = window.innerWidth;height = window.innerHeight;} else {var rect = this.element.getBoundingClientRect();width = rect.width;height = rect.height;}this.setSize( width, height );
};// ----- render ----- //Illustration.prototype.renderGraph = function( item ) {if ( this.isCanvas ) {this.renderGraphCanvas( item );} else if ( this.isSvg ) {this.renderGraphSvg( item );}
};// combo method
Illustration.prototype.updateRenderGraph = function( item ) {this.updateGraph();this.renderGraph( item );
};// ----- canvas ----- //Illustration.prototype.setCanvas = function( element ) {this.element = element;this.isCanvas = true;// update related propertiesthis.ctx = this.element.getContext('2d');// set initial sizethis.setSizeCanvas( element.width, element.height );
};Illustration.prototype.setSizeCanvas = function( width, height ) {this.width = width;this.height = height;// up-rez for hi-DPI devicesvar pixelRatio = this.pixelRatio = window.devicePixelRatio || 1;this.element.width = this.canvasWidth = width * pixelRatio;this.element.height = this.canvasHeight = height * pixelRatio;var needsHighPixelRatioSizing = pixelRatio > 1 && !this.resize;if ( needsHighPixelRatioSizing ) {this.element.style.width = width + 'px';this.element.style.height = height + 'px';}
};Illustration.prototype.renderGraphCanvas = function( item ) {item = item || this;this.prerenderCanvas();Anchor.prototype.renderGraphCanvas.call( item, this.ctx );this.postrenderCanvas();
};Illustration.prototype.prerenderCanvas = function() {var ctx = this.ctx;ctx.lineCap = 'round';ctx.lineJoin = 'round';ctx.clearRect( 0, 0, this.canvasWidth, this.canvasHeight );ctx.save();if ( this.centered ) {var centerX = this.width/2 * this.pixelRatio;var centerY = this.height/2 * this.pixelRatio;ctx.translate( centerX, centerY );}var scale = this.pixelRatio * this.zoom;ctx.scale( scale, scale );this.onPrerender( ctx );
};Illustration.prototype.postrenderCanvas = function() {this.ctx.restore();
};// ----- svg ----- //Illustration.prototype.setSvg = function( element ) {this.element = element;this.isSvg = true;this.pixelRatio = 1;// set initial size from width & height attributesvar width = element.getAttribute('width');var height = element.getAttribute('height');this.setSizeSvg( width, height );
};Illustration.prototype.setSizeSvg = function( width, height ) {this.width = width;this.height = height;var viewWidth = width / this.zoom;var viewHeight = height / this.zoom;var viewX = this.centered ? -viewWidth/2 : 0;var viewY = this.centered ? -viewHeight/2 : 0;this.element.setAttribute( 'viewBox', viewX + ' ' + viewY + ' ' +viewWidth + ' ' + viewHeight );if ( this.resize ) {// remove size attributes, let size be determined by viewboxthis.element.removeAttribute('width');this.element.removeAttribute('height');} else {this.element.setAttribute( 'width', width );this.element.setAttribute( 'height', height );}
};Illustration.prototype.renderGraphSvg = function( item ) {item = item || this;empty( this.element );this.onPrerender( this.element );Anchor.prototype.renderGraphSvg.call( item, this.element );
};function empty( element ) {while ( element.firstChild ) {element.removeChild( element.firstChild );}
}// ----- drag ----- //Illustration.prototype.setDragRotate = function( item ) {if ( !item ) {return;} else if ( item === true ) {/* eslint consistent-this: "off" */item = this;}this.dragRotate = item;this.bindDrag( this.element );
};Illustration.prototype.dragStart = function(/* event, pointer */) {this.dragStartRX = this.dragRotate.rotate.x;this.dragStartRY = this.dragRotate.rotate.y;Dragger.prototype.dragStart.apply( this, arguments );
};Illustration.prototype.dragMove = function( event, pointer ) {var moveX = pointer.pageX - this.dragStartX;var moveY = pointer.pageY - this.dragStartY;var displaySize = Math.min( this.width, this.height );var moveRY = moveX / displaySize * TAU;var moveRX = moveY / displaySize * TAU;this.dragRotate.rotate.x = this.dragStartRX - moveRX;this.dragRotate.rotate.y = this.dragStartRY - moveRY;Dragger.prototype.dragMove.apply( this, arguments );
};return Illustration;}));
/*** PathCommand*/( function( root, factory ) {// module definitionif ( typeof module == 'object' && module.exports ) {// CommonJSmodule.exports = factory( require('./vector') );} else {// browser globalvar Zdog = root.Zdog;Zdog.PathCommand = factory( Zdog.Vector );}
}( this, function factory( Vector ) {function PathCommand( method, points, previousPoint ) {this.method = method;this.points = points.map( mapVectorPoint );this.renderPoints = points.map( mapNewVector );this.previousPoint = previousPoint;this.endRenderPoint = this.renderPoints[ this.renderPoints.length - 1 ];// arc actions come with previous point & corner point// but require bezier control pointsif ( method == 'arc' ) {this.controlPoints = [ new Vector(), new Vector() ];}
}function mapVectorPoint( point ) {if ( point instanceof Vector ) {return point;} else {return new Vector( point );}
}function mapNewVector( point ) {return new Vector( point );
}PathCommand.prototype.reset = function() {// reset renderPoints back to orignal points positionvar points = this.points;this.renderPoints.forEach( function( renderPoint, i ) {var point = points[i];renderPoint.set( point );});
};PathCommand.prototype.transform = function( translation, rotation, scale ) {this.renderPoints.forEach( function( renderPoint ) {renderPoint.transform( translation, rotation, scale );});
};PathCommand.prototype.render = function( ctx, elem, renderer ) {return this[ this.method ]( ctx, elem, renderer );
};PathCommand.prototype.move = function( ctx, elem, renderer ) {return renderer.move( ctx, elem, this.renderPoints[0] );
};PathCommand.prototype.line = function( ctx, elem, renderer ) {return renderer.line( ctx, elem, this.renderPoints[0] );
};PathCommand.prototype.bezier = function( ctx, elem, renderer ) {var cp0 = this.renderPoints[0];var cp1 = this.renderPoints[1];var end = this.renderPoints[2];return renderer.bezier( ctx, elem, cp0, cp1, end );
};var arcHandleLength = 9/16;PathCommand.prototype.arc = function( ctx, elem, renderer ) {var prev = this.previousPoint;var corner = this.renderPoints[0];var end = this.renderPoints[1];var cp0 = this.controlPoints[0];var cp1 = this.controlPoints[1];cp0.set( prev ).lerp( corner, arcHandleLength );cp1.set( end ).lerp( corner, arcHandleLength );return renderer.bezier( ctx, elem, cp0, cp1, end );
};return PathCommand;}));
/*** Shape*/( function( root, factory ) {// module definitionif ( typeof module == 'object' && module.exports ) {// CommonJSmodule.exports = factory( require('./boilerplate'), require('./vector'),require('./path-command'), require('./anchor') );} else {// browser globalvar Zdog = root.Zdog;Zdog.Shape = factory( Zdog, Zdog.Vector, Zdog.PathCommand, Zdog.Anchor );}
}( this, function factory( utils, Vector, PathCommand, Anchor ) {var Shape = Anchor.subclass({stroke: 1,fill: false,color: '#333',closed: true,visible: true,path: [ {} ],front: { z: 1 },backface: true,
});Shape.prototype.create = function( options ) {Anchor.prototype.create.call( this, options );this.updatePath();// frontthis.front = new Vector( options.front || this.front );this.renderFront = new Vector( this.front );this.renderNormal = new Vector();
};var actionNames = ['move','line','bezier','arc',
];Shape.prototype.updatePath = function() {this.setPath();this.updatePathCommands();
};// place holder for Ellipse, Rect, etc.
Shape.prototype.setPath = function() {};// parse path into PathCommands
Shape.prototype.updatePathCommands = function() {var previousPoint;this.pathCommands = this.path.map( function( pathPart, i ) {// pathPart can be just vector coordinates -> { x, y, z }// or path instruction -> { arc: [ {x0,y0,z0}, {x1,y1,z1} ] }var keys = Object.keys( pathPart );var method = keys[0];var points = pathPart[ method ];// default to line if no instructionvar isInstruction = keys.length == 1 && actionNames.indexOf( method ) != -1;if ( !isInstruction ) {method = 'line';points = pathPart;}// munge single-point methods like line & move without arraysvar isLineOrMove = method == 'line' || method == 'move';var isPointsArray = Array.isArray( points );if ( isLineOrMove && !isPointsArray ) {points = [ points ];}// first action is always movemethod = i === 0 ? 'move' : method;// arcs require previous last pointvar command = new PathCommand( method, points, previousPoint );// update previousLastPointpreviousPoint = command.endRenderPoint;return command;});
};// ----- update ----- //Shape.prototype.reset = function() {this.renderOrigin.set( this.origin );this.renderFront.set( this.front );// reset command render pointsthis.pathCommands.forEach( function( command ) {command.reset();});
};Shape.prototype.transform = function( translation, rotation, scale ) {// calculate render points backface visibility & cone/hemisphere shapesthis.renderOrigin.transform( translation, rotation, scale );this.renderFront.transform( translation, rotation, scale );this.renderNormal.set( this.renderOrigin ).subtract( this.renderFront );// transform pointsthis.pathCommands.forEach( function( command ) {command.transform( translation, rotation, scale );});// transform childrenthis.children.forEach( function( child ) {child.transform( translation, rotation, scale );});
};Shape.prototype.updateSortValue = function() {// sort by average z of all points// def not geometrically correct, but works for mevar pointCount = this.pathCommands.length;var firstPoint = this.pathCommands[0].endRenderPoint;var lastPoint = this.pathCommands[ pointCount - 1 ].endRenderPoint;// ignore the final point if self closing shapevar isSelfClosing = pointCount > 2 && firstPoint.isSame( lastPoint );if ( isSelfClosing ) {pointCount -= 1;}var sortValueTotal = 0;for ( var i = 0; i < pointCount; i++ ) {sortValueTotal += this.pathCommands[i].endRenderPoint.z;}this.sortValue = sortValueTotal / pointCount;
};// ----- render ----- //Shape.prototype.render = function( ctx, renderer ) {var length = this.pathCommands.length;if ( !this.visible || !length ) {return;}// do not render if hiding backfacethis.isFacingBack = this.renderNormal.z > 0;if ( !this.backface && this.isFacingBack ) {return;}if ( !renderer ) {throw new Error( 'Zdog renderer required. Set to ' + renderer );}// render dot or pathvar isDot = length == 1;if ( renderer.isCanvas && isDot ) {this.renderCanvasDot( ctx, renderer );} else {this.renderPath( ctx, renderer );}
};var TAU = utils.TAU;
// Safari does not render lines with no size, have to render circle instead
Shape.prototype.renderCanvasDot = function( ctx ) {var lineWidth = this.getLineWidth();if ( !lineWidth ) {return;}ctx.fillStyle = this.getRenderColor();var point = this.pathCommands[0].endRenderPoint;ctx.beginPath();var radius = lineWidth/2;ctx.arc( point.x, point.y, radius, 0, TAU );ctx.fill();
};Shape.prototype.getLineWidth = function() {if ( !this.stroke ) {return 0;}if ( this.stroke == true ) {return 1;}return this.stroke;
};Shape.prototype.getRenderColor = function() {// use backface color if applicablevar isBackfaceColor = typeof this.backface == 'string' && this.isFacingBack;var color = isBackfaceColor ? this.backface : this.color;return color;
};Shape.prototype.renderPath = function( ctx, renderer ) {var elem = this.getRenderElement( ctx, renderer );var isTwoPoints = this.pathCommands.length == 2 &&this.pathCommands[1].method == 'line';var isClosed = !isTwoPoints && this.closed;var color = this.getRenderColor();renderer.renderPath( ctx, elem, this.pathCommands, isClosed );renderer.stroke( ctx, elem, this.stroke, color, this.getLineWidth() );renderer.fill( ctx, elem, this.fill, color );renderer.end( ctx, elem );
};var svgURI = 'http://www.w3.org/2000/svg';Shape.prototype.getRenderElement = function( ctx, renderer ) {if ( !renderer.isSvg ) {return;}if ( !this.svgElement ) {// create svgElementthis.svgElement = document.createElementNS( svgURI, 'path');this.svgElement.setAttribute( 'stroke-linecap', 'round' );this.svgElement.setAttribute( 'stroke-linejoin', 'round' );}return this.svgElement;
};return Shape;}));
/*** Group*/( function( root, factory ) {// module definitionif ( typeof module == 'object' && module.exports ) {// CommonJSmodule.exports = factory( require('./anchor') );} else {// browser globalvar Zdog = root.Zdog;Zdog.Group = factory( Zdog.Anchor );}
}( this, function factory( Anchor ) {var Group = Anchor.subclass({updateSort: false,visible: true,
});// ----- update ----- //Group.prototype.updateSortValue = function() {var sortValueTotal = 0;this.flatGraph.forEach( function( item ) {item.updateSortValue();sortValueTotal += item.sortValue;});// average sort value of all points// def not geometrically correct, but works for methis.sortValue = sortValueTotal / this.flatGraph.length;if ( this.updateSort ) {this.flatGraph.sort( Anchor.shapeSorter );}
};// ----- render ----- //Group.prototype.render = function( ctx, renderer ) {if ( !this.visible ) {return;}this.flatGraph.forEach( function( item ) {item.render( ctx, renderer );});
};// actual group flatGraph only used inside group
Group.prototype.updateFlatGraph = function() {// do not include selfvar flatGraph = [];this.flatGraph = this.addChildFlatGraph( flatGraph );
};// do not include children, group handles rendering & sorting internally
Group.prototype.getFlatGraph = function() {return [ this ];
};return Group;}));
/*** Rect*/( function( root, factory ) {// module definitionif ( typeof module == 'object' && module.exports ) {// CommonJSmodule.exports = factory( require('./shape') );} else {// browser globalvar Zdog = root.Zdog;Zdog.Rect = factory( Zdog.Shape );}
}( this, function factory( Shape ) {var Rect = Shape.subclass({width: 1,height: 1,
});Rect.prototype.setPath = function() {var x = this.width / 2;var y = this.height / 2;/* eslint key-spacing: "off" */this.path = [{ x: -x, y: -y },{ x:  x, y: -y },{ x:  x, y:  y },{ x: -x, y:  y },];
};return Rect;}));
/*** RoundedRect*/( function( root, factory ) {// module definitionif ( typeof module == 'object' && module.exports ) {// CommonJSmodule.exports = factory( require('./shape') );} else {// browser globalvar Zdog = root.Zdog;Zdog.RoundedRect = factory( Zdog.Shape );}
}( this, function factory( Shape ) {var RoundedRect = Shape.subclass({width: 1,height: 1,cornerRadius: 0.25,closed: false,
});RoundedRect.prototype.setPath = function() {/* eslintid-length: [ "error", { "min": 2, "exceptions": [ "x", "y" ] }],key-spacing: "off" */var xA = this.width / 2;var yA = this.height / 2;var shortSide = Math.min( xA, yA );var cornerRadius = Math.min( this.cornerRadius, shortSide );var xB = xA - cornerRadius;var yB = yA - cornerRadius;var path = [// top right corner{ x: xB, y: -yA },{ arc: [{ x: xA, y: -yA },{ x: xA, y: -yB },]},];// bottom right cornerif ( yB ) {path.push({ x: xA, y: yB });}path.push({ arc: [{ x: xA, y:  yA },{ x: xB, y:  yA },]});// bottom left cornerif ( xB ) {path.push({ x: -xB, y: yA });}path.push({ arc: [{ x: -xA, y:  yA },{ x: -xA, y:  yB },]});// top left cornerif ( yB ) {path.push({ x: -xA, y: -yB });}path.push({ arc: [{ x: -xA, y: -yA },{ x: -xB, y: -yA },]});// back to top right cornerif ( xB ) {path.push({ x: xB, y: -yA });}this.path = path;
};return RoundedRect;}));
/*** Ellipse*/( function( root, factory ) {// module definitionif ( typeof module == 'object' && module.exports ) {// CommonJSmodule.exports = factory( require('./shape') );} else {// browser globalvar Zdog = root.Zdog;Zdog.Ellipse = factory( Zdog.Shape );}}( this, function factory( Shape ) {var Ellipse = Shape.subclass({diameter: 1,width: undefined,height: undefined,quarters: 4,closed: false,
});Ellipse.prototype.setPath = function() {var width = this.width != undefined ? this.width : this.diameter;var height = this.height != undefined ? this.height : this.diameter;var x = width / 2;var y = height / 2;this.path = [{ x: 0, y: -y },{ arc: [ // top right{ x: x, y: -y },{ x: x, y: 0 },]},];// bottom rightif ( this.quarters > 1 ) {this.path.push({ arc: [{ x: x, y: y },{ x: 0, y: y },]});}// bottom leftif ( this.quarters > 2 ) {this.path.push({ arc: [{ x: -x, y: y },{ x: -x, y: 0 },]});}// top leftif ( this.quarters > 3 ) {this.path.push({ arc: [{ x: -x, y: -y },{ x: 0, y: -y },]});}
};return Ellipse;}));
/*** Shape*/( function( root, factory ) {// module definitionif ( typeof module == 'object' && module.exports ) {// CommonJSmodule.exports = factory( require('./boilerplate'), require('./shape') );} else {// browser globalvar Zdog = root.Zdog;Zdog.Polygon = factory( Zdog, Zdog.Shape );}
}( this, function factory( utils, Shape ) {var Polygon = Shape.subclass({sides: 3,radius: 0.5,
});var TAU = utils.TAU;Polygon.prototype.setPath = function() {this.path = [];for ( var i=0; i < this.sides; i++ ) {var theta = i/this.sides * TAU - TAU/4;var x = Math.cos( theta ) * this.radius;var y = Math.sin( theta ) * this.radius;this.path.push({ x: x, y: y });}
};return Polygon;}));
/*** Hemisphere composite shape*/( function( root, factory ) {// module definitionif ( typeof module == 'object' && module.exports ) {// CommonJSmodule.exports = factory( require('./boilerplate'), require('./vector'),require('./anchor'), require('./ellipse') );} else {// browser globalvar Zdog = root.Zdog;Zdog.Hemisphere = factory( Zdog, Zdog.Vector, Zdog.Anchor, Zdog.Ellipse );}
}( this, function factory( utils, Vector, Anchor, Ellipse ) {var Hemisphere = Ellipse.subclass({fill: true,
});var TAU = utils.TAU;Hemisphere.prototype.create = function(/* options */) {// call superEllipse.prototype.create.apply( this, arguments );// composite shape, create child shapesthis.apex = new Anchor({addTo: this,translate: { z: this.diameter/2 },});// vector used for calculationthis.renderCentroid = new Vector();
};Hemisphere.prototype.updateSortValue = function() {// centroid of hemisphere is 3/8 between origin and apexthis.renderCentroid.set( this.renderOrigin ).lerp( this.apex.renderOrigin, 3/8 );this.sortValue = this.renderCentroid.z;
};Hemisphere.prototype.render = function( ctx, renderer ) {this.renderDome( ctx, renderer );// call superEllipse.prototype.render.apply( this, arguments );
};Hemisphere.prototype.renderDome = function( ctx, renderer ) {if ( !this.visible ) {return;}var elem = this.getDomeRenderElement( ctx, renderer );var contourAngle = Math.atan2( this.renderNormal.y, this.renderNormal.x );var domeRadius = this.diameter/2 * this.renderNormal.magnitude();var x = this.renderOrigin.x;var y = this.renderOrigin.y;if ( renderer.isCanvas ) {// canvasvar startAngle = contourAngle + TAU/4;var endAngle = contourAngle - TAU/4;ctx.beginPath();ctx.arc( x, y, domeRadius, startAngle, endAngle );} else if ( renderer.isSvg ) {// svgcontourAngle = (contourAngle - TAU/4) / TAU * 360;this.domeSvgElement.setAttribute( 'd', 'M ' + -domeRadius + ',0 A ' +domeRadius + ',' + domeRadius + ' 0 0 1 ' + domeRadius + ',0' );this.domeSvgElement.setAttribute( 'transform','translate(' + x + ',' + y + ' ) rotate(' + contourAngle + ')' );}renderer.stroke( ctx, elem, this.stroke, this.color, this.getLineWidth() );renderer.fill( ctx, elem, this.fill, this.color );renderer.end( ctx, elem );
};var svgURI = 'http://www.w3.org/2000/svg';Hemisphere.prototype.getDomeRenderElement = function( ctx, renderer ) {if ( !renderer.isSvg ) {return;}if ( !this.domeSvgElement ) {// create svgElementthis.domeSvgElement = document.createElementNS( svgURI, 'path');this.domeSvgElement.setAttribute( 'stroke-linecap', 'round' );this.domeSvgElement.setAttribute( 'stroke-linejoin', 'round' );}return this.domeSvgElement;
};return Hemisphere;}));
/*** Cylinder composite shape*/( function( root, factory ) {// module definitionif ( typeof module == 'object' && module.exports ) {// CommonJSmodule.exports = factory( require('./boilerplate'),require('./path-command'), require('./shape'), require('./group'),require('./ellipse') );} else {// browser globalvar Zdog = root.Zdog;Zdog.Cylinder = factory( Zdog, Zdog.PathCommand, Zdog.Shape,Zdog.Group, Zdog.Ellipse );}
}( this, function factory( utils, PathCommand, Shape, Group, Ellipse ) {function noop() {}// ----- CylinderGroup ----- //var CylinderGroup = Group.subclass({color: '#333',updateSort: true,
});CylinderGroup.prototype.create = function() {Group.prototype.create.apply( this, arguments );this.pathCommands = [new PathCommand( 'move', [ {} ] ),new PathCommand( 'line', [ {} ] ),];
};CylinderGroup.prototype.render = function( ctx, renderer ) {this.renderCylinderSurface( ctx, renderer );Group.prototype.render.apply( this, arguments );
};CylinderGroup.prototype.renderCylinderSurface = function( ctx, renderer ) {if ( !this.visible ) {return;}// render cylinder surfacevar elem = this.getRenderElement( ctx, renderer );var frontBase = this.frontBase;var rearBase = this.rearBase;var scale = frontBase.renderNormal.magnitude();var strokeWidth = frontBase.diameter * scale + frontBase.getLineWidth();// set path command render pointsthis.pathCommands[0].renderPoints[0].set( frontBase.renderOrigin );this.pathCommands[1].renderPoints[0].set( rearBase.renderOrigin );if ( renderer.isCanvas ) {ctx.lineCap = 'butt'; // nice}renderer.renderPath( ctx, elem, this.pathCommands );renderer.stroke( ctx, elem, true, this.color, strokeWidth );renderer.end( ctx, elem );if ( renderer.isCanvas ) {ctx.lineCap = 'round'; // reset}
};var svgURI = 'http://www.w3.org/2000/svg';CylinderGroup.prototype.getRenderElement = function( ctx, renderer ) {if ( !renderer.isSvg ) {return;}if ( !this.svgElement ) {// create svgElementthis.svgElement = document.createElementNS( svgURI, 'path');}return this.svgElement;
};// prevent double-creation in parent.copyGraph()
// only create in Cylinder.create()
CylinderGroup.prototype.copyGraph = noop;// ----- CylinderEllipse ----- //var CylinderEllipse = Ellipse.subclass();CylinderEllipse.prototype.copyGraph = noop;// ----- Cylinder ----- //var Cylinder = Shape.subclass({diameter: 1,length: 1,frontFace: undefined,fill: true,
});var TAU = utils.TAU;Cylinder.prototype.create = function(/* options */) {// call superShape.prototype.create.apply( this, arguments );// composite shape, create child shapes// CylinderGroup to render cylinder surface then basesthis.group = new CylinderGroup({addTo: this,color: this.color,visible: this.visible,});var baseZ = this.length/2;var baseColor = this.backface || true;// front outside basethis.frontBase = this.group.frontBase = new Ellipse({addTo: this.group,diameter: this.diameter,translate: { z: baseZ },rotate: { y: TAU/2 },color: this.color,stroke: this.stroke,fill: this.fill,backface: this.frontFace || baseColor,visible: this.visible,});// back outside basethis.rearBase = this.group.rearBase = this.frontBase.copy({translate: { z: -baseZ },rotate: { y: 0 },backface: baseColor,});
};// Cylinder shape does not render anything
Cylinder.prototype.render = function() {};// ----- set child properties ----- //var childProperties = [ 'stroke', 'fill', 'color', 'visible' ];
childProperties.forEach( function( property ) {// use proxy property for custom getter & settervar _prop = '_' + property;Object.defineProperty( Cylinder.prototype, property, {get: function() {return this[ _prop ];},set: function( value ) {this[ _prop ] = value;// set property on childrenif ( this.frontBase ) {this.frontBase[ property ] = value;this.rearBase[ property ] = value;this.group[ property ] = value;}},});
});// TODO child property setter for backface, frontBaseColor, & rearBaseColorreturn Cylinder;}));
/*** Cone composite shape*/( function( root, factory ) {// module definitionif ( typeof module == 'object' && module.exports ) {// CommonJSmodule.exports = factory( require('./boilerplate'), require('./vector'),require('./path-command'), require('./anchor'), require('./ellipse') );} else {// browser globalvar Zdog = root.Zdog;Zdog.Cone = factory( Zdog, Zdog.Vector, Zdog.PathCommand,Zdog.Anchor, Zdog.Ellipse );}
}( this, function factory( utils, Vector, PathCommand, Anchor, Ellipse ) {var Cone = Ellipse.subclass({length: 1,fill: true,
});var TAU = utils.TAU;Cone.prototype.create = function(/* options */) {// call superEllipse.prototype.create.apply( this, arguments );// composite shape, create child shapesthis.apex = new Anchor({addTo: this,translate: { z: this.length },});// vectors used for calculationthis.renderApex = new Vector();this.renderCentroid = new Vector();this.tangentA = new Vector();this.tangentB = new Vector();this.surfacePathCommands = [new PathCommand( 'move', [ {} ] ), // points set in renderConeSurfacenew PathCommand( 'line', [ {} ] ),new PathCommand( 'line', [ {} ] ),];
};Cone.prototype.updateSortValue = function() {// center of cone is one third of its lengththis.renderCentroid.set( this.renderOrigin ).lerp( this.apex.renderOrigin, 1/3 );this.sortValue = this.renderCentroid.z;
};Cone.prototype.render = function( ctx, renderer ) {this.renderConeSurface( ctx, renderer );Ellipse.prototype.render.apply( this, arguments );
};Cone.prototype.renderConeSurface = function( ctx, renderer ) {if ( !this.visible ) {return;}this.renderApex.set( this.apex.renderOrigin ).subtract( this.renderOrigin );var scale = this.renderNormal.magnitude();var apexDistance = this.renderApex.magnitude2d();var normalDistance = this.renderNormal.magnitude2d();// eccentricityvar eccenAngle = Math.acos( normalDistance / scale );var eccen = Math.sin( eccenAngle );var radius = this.diameter/2 * scale;// does apex extend beyond eclipse of facevar isApexVisible = radius * eccen < apexDistance;if ( !isApexVisible ) {return;}// update tangentsvar apexAngle = Math.atan2( this.renderNormal.y, this.renderNormal.x ) +TAU/2;var projectLength = apexDistance / eccen;var projectAngle = Math.acos( radius / projectLength );// set tangent pointsvar tangentA = this.tangentA;var tangentB = this.tangentB;tangentA.x = Math.cos( projectAngle ) * radius * eccen;tangentA.y = Math.sin( projectAngle ) * radius;tangentB.set( this.tangentA );tangentB.y *= -1;tangentA.rotateZ( apexAngle );tangentB.rotateZ( apexAngle );tangentA.add( this.renderOrigin );tangentB.add( this.renderOrigin );this.setSurfaceRenderPoint( 0, tangentA );this.setSurfaceRenderPoint( 1, this.apex.renderOrigin );this.setSurfaceRenderPoint( 2, tangentB );// rendervar elem = this.getSurfaceRenderElement( ctx, renderer );renderer.renderPath( ctx, elem, this.surfacePathCommands );renderer.stroke( ctx, elem, this.stroke, this.color, this.getLineWidth() );renderer.fill( ctx, elem, this.fill, this.color );renderer.end( ctx, elem );
};var svgURI = 'http://www.w3.org/2000/svg';Cone.prototype.getSurfaceRenderElement = function( ctx, renderer ) {if ( !renderer.isSvg ) {return;}if ( !this.surfaceSvgElement ) {// create svgElementthis.surfaceSvgElement = document.createElementNS( svgURI, 'path');this.surfaceSvgElement.setAttribute( 'stroke-linecap', 'round' );this.surfaceSvgElement.setAttribute( 'stroke-linejoin', 'round' );}return this.surfaceSvgElement;
};Cone.prototype.setSurfaceRenderPoint = function( index, point ) {var renderPoint = this.surfacePathCommands[ index ].renderPoints[0];renderPoint.set( point );
};return Cone;}));
/*** Box composite shape*/( function( root, factory ) {// module definitionif ( typeof module == 'object' && module.exports ) {// CommonJSmodule.exports = factory( require('./boilerplate'), require('./anchor'),require('./shape'), require('./rect') );} else {// browser globalvar Zdog = root.Zdog;Zdog.Box = factory( Zdog, Zdog.Anchor, Zdog.Shape, Zdog.Rect );}
}( this, function factory( utils, Anchor, Shape, Rect ) {// ----- BoxRect ----- //var BoxRect = Rect.subclass();
// prevent double-creation in parent.copyGraph()
// only create in Box.create()
BoxRect.prototype.copyGraph = function() {};// ----- Box ----- //var TAU = utils.TAU;
var faceNames = ['frontFace','rearFace','leftFace','rightFace','topFace','bottomFace',
];var boxDefaults = utils.extend( {}, Shape.defaults );
delete boxDefaults.path;
faceNames.forEach( function( faceName ) {boxDefaults[ faceName ] = true;
});
utils.extend( boxDefaults, {width: 1,height: 1,depth: 1,fill: true,
});var Box = Anchor.subclass( boxDefaults );Box.prototype.create = function( options ) {Anchor.prototype.create.call( this, options );this.updatePath();// HACK reset fill to trigger face setterthis.fill = this.fill;
};Box.prototype.updatePath = function() {// reset all faces to trigger settersfaceNames.forEach( function( faceName ) {this[ faceName ] = this[ faceName ];}, this );
};faceNames.forEach( function( faceName ) {var _faceName = '_' + faceName;Object.defineProperty( Box.prototype, faceName, {get: function() {return this[ _faceName ];},set: function( value ) {this[ _faceName ] = value;this.setFace( faceName, value );},});
});Box.prototype.setFace = function( faceName, value ) {var rectProperty = faceName + 'Rect';var rect = this[ rectProperty ];// remove if falseif ( !value ) {this.removeChild( rect );return;}// update & add facevar options = this.getFaceOptions( faceName );options.color = typeof value == 'string' ? value : this.color;if ( rect ) {// update previousrect.setOptions( options );} else {// create newrect = this[ rectProperty ] = new BoxRect( options );}rect.updatePath();this.addChild( rect );
};Box.prototype.getFaceOptions = function( faceName ) {return {frontFace: {width: this.width,height: this.height,translate: { z: this.depth/2 },},rearFace: {width: this.width,height: this.height,translate: { z: -this.depth/2 },rotate: { y: TAU/2 },},leftFace: {width: this.depth,height: this.height,translate: { x: -this.width/2 },rotate: { y: -TAU/4 },},rightFace: {width: this.depth,height: this.height,translate: { x: this.width/2 },rotate: { y: TAU/4 },},topFace: {width: this.width,height: this.depth,translate: { y: -this.height/2 },rotate: { x: -TAU/4 },},bottomFace: {width: this.width,height: this.depth,translate: { y: this.height/2 },rotate: { x: TAU/4 },},}[ faceName ];
};// ----- set face properties ----- //var childProperties = [ 'color', 'stroke', 'fill', 'backface', 'front','visible' ];
childProperties.forEach( function( property ) {// use proxy property for custom getter & settervar _prop = '_' + property;Object.defineProperty( Box.prototype, property, {get: function() {return this[ _prop ];},set: function( value ) {this[ _prop ] = value;faceNames.forEach( function( faceName ) {var rect = this[ faceName + 'Rect' ];var isFaceColor = typeof this[ faceName ] == 'string';var isColorUnderwrite = property == 'color' && isFaceColor;if ( rect && !isColorUnderwrite ) {rect[ property ] = value;}}, this );},});
});return Box;}));
/*** Index*/( function( root, factory ) {// module definitionif ( typeof module == 'object' && module.exports ) {// CommonJSmodule.exports = factory(require('./boilerplate'),require('./canvas-renderer'),require('./svg-renderer'),require('./vector'),require('./anchor'),require('./dragger'),require('./illustration'),require('./path-command'),require('./shape'),require('./group'),require('./rect'),require('./rounded-rect'),require('./ellipse'),require('./polygon'),require('./hemisphere'),require('./cylinder'),require('./cone'),require('./box'));} else if ( typeof define == 'function' && define.amd ) {/* globals define */ // AMDdefine( 'zdog', [], root.Zdog );}
})( this, function factory( Zdog, CanvasRenderer, SvgRenderer, Vector, Anchor,Dragger, Illustration, PathCommand, Shape, Group, Rect, RoundedRect,Ellipse, Polygon, Hemisphere, Cylinder, Cone, Box ) {Zdog.CanvasRenderer = CanvasRenderer;Zdog.SvgRenderer = SvgRenderer;Zdog.Vector = Vector;Zdog.Anchor = Anchor;Zdog.Dragger = Dragger;Zdog.Illustration = Illustration;Zdog.PathCommand = PathCommand;Zdog.Shape = Shape;Zdog.Group = Group;Zdog.Rect = Rect;Zdog.RoundedRect = RoundedRect;Zdog.Ellipse = Ellipse;Zdog.Polygon = Polygon;Zdog.Hemisphere = Hemisphere;Zdog.Cylinder = Cylinder;Zdog.Cone = Cone;Zdog.Box = Box;return Zdog;
});
const radiansToDegrees = (_val) => {  return _val * (Math.PI/180);
}const componentToHex = (c) => {var hex = c.toString(16);return hex.length == 1 ? "0" + hex : hex;
}const rgbToHex = (r, g, b) => {return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}const hslToRgb = (_h, s, l) => {	var h = Math.min(_h, 359)/60;var c = (1-Math.abs((2*l)-1))*s;var x = c*(1-Math.abs((h % 2)-1));var m = l - (0.5*c);var r = m, g = m, b = m;if (h < 1) {r += c, g = +x, b += 0;} else if (h < 2) {r += x, g += c, b += 0;} else if (h < 3) {r += 0, g += c, b += x;} else if (h < 4) {r += 0, g += x, b += c;} else if (h < 5) {r += x, g += 0, b += c;} else if (h < 6) {r += c, g += 0, b += x;} else {r = 0, g = 0, b = 0;}//return 'rgb(' + Math.floor(r*255) + ', ' + Math.floor(g*255) + ', ' + Math.floor(b*255) + ')';return rgbToHex(Math.floor(r*255), Math.floor(g*255) , Math.floor(b*255) );
}const createSpectrum = (length) => {var colors = [];// 270 if we don't want the spectrum to loop// 360 if we do want the spectrum to loopvar step = 360/length;for (var i = 1; i <= length; i++) {var color = hslToRgb((i)*step, 0.5, 0.5);colors.push(color);}return colors;
}
//48 because we have 4 faces to populate x 12 cubes
const spectrum = createSpectrum(48).reverse();let illo = new Zdog.Illustration({element: '.zdog-canvas',dragRotate: true
});var mainGroup = new Zdog.Anchor({addTo: illo,translate: {x: -0, y: 0, z: 0 }
});var boxGroup = new Zdog.Group({addTo: mainGroup,translate: {x: -400, y: 0, z: 0 }
});const makeBoxes = () => {let leftFaceCount = 0, topFaceCount = 11, rightFaceCount = 23,  bottomFaceCount = 35;for(let i = 0; i < 12; i++){let boxAnchor = new Zdog.Anchor({addTo: mainGroup,rotate: {  y: radiansToDegrees(360 - (i * 30))},translate: {x: 0, y: 0, z: 0 }});		let box = new Zdog.Box({addTo: boxAnchor,translate: {x: -200},width: 60,height: 60,depth: 60,stroke: 2,color: '#000',// remove left & right facesleftFace: spectrum[leftFaceCount],rightFace: spectrum[rightFaceCount],rearFace: '#000',topFace: spectrum[topFaceCount],bottomFace: spectrum[bottomFaceCount],backface: true,rotate: { z: radiansToDegrees((90/12) * i)}})leftFaceCount ++;rightFaceCount ++;topFaceCount ++;bottomFaceCount ++;}
}mainGroup.rotate.x = radiansToDegrees(-45);function render () {for(let i = 1; i < mainGroup.children.length; i++) {mainGroup.children[i].children[0].rotate.z -= (0.031)}illo.updateRenderGraph();requestAnimationFrame(render)
}makeBoxes();
render();

canvas烟花效果

效果图

在这里插入图片描述

完整代码

HTML

<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>HTML5 Canvas烟花绽放庆祝活动场景DEMO演示</title><link rel="stylesheet" href="css/style.css"></head>
<body>
<div style="text-align:center;clear:both;">
<script src="/gg_bd_ad_720x90.js" type="text/javascript"></script>
<script src="/follow.js" type="text/javascript"></script>
</div>
<canvas></canvas>
<canvas></canvas>
<canvas></canvas><script  src="js/index.js"></script></body>
</html>

CSS

body {margin: 0;background: black;
}
canvas {position: absolute;
}

JavaScript

// CLASSES
class Shard {constructor(x, y, hue) {this.x = x;this.y = y;this.hue = hue;this.lightness = 50;this.size = 15 + Math.random() * 10;const angle = Math.random() * 2 * Math.PI;const blastSpeed = 1 + Math.random() * 6;this.xSpeed = Math.cos(angle) * blastSpeed;this.ySpeed = Math.sin(angle) * blastSpeed;this.target = getTarget();this.ttl = 100;this.timer = 0;}draw() {ctx2.fillStyle = `hsl(${this.hue}, 100%, ${this.lightness}%)`;ctx2.beginPath();ctx2.arc(this.x, this.y, this.size, 0, 2 * Math.PI);ctx2.closePath();ctx2.fill();}update() {if (this.target) {const dx = this.target.x - this.x;const dy = this.target.y - this.y;const dist = Math.sqrt(dx * dx + dy * dy);const a = Math.atan2(dy, dx);const tx = Math.cos(a) * 5;const ty = Math.sin(a) * 5;this.size = lerp(this.size, 1.5, 0.05);if (dist < 5) {this.lightness = lerp(this.lightness, 100, 0.01);this.xSpeed = this.ySpeed = 0;this.x = lerp(this.x, this.target.x + fidelity / 2, 0.05);this.y = lerp(this.y, this.target.y + fidelity / 2, 0.05);this.timer += 1;} elseif (dist < 10) {this.lightness = lerp(this.lightness, 100, 0.01);this.xSpeed = lerp(this.xSpeed, tx, 0.1);this.ySpeed = lerp(this.ySpeed, ty, 0.1);this.timer += 1;} else{this.xSpeed = lerp(this.xSpeed, tx, 0.02);this.ySpeed = lerp(this.ySpeed, ty, 0.02);}} else{this.ySpeed += 0.05;//this.xSpeed = lerp(this.xSpeed, 0, 0.1);this.size = lerp(this.size, 1, 0.05);if (this.y > c2.height) {shards.forEach((shard, idx) => {if (shard === this) {shards.splice(idx, 1);}});}}this.x = this.x + this.xSpeed;this.y = this.y + this.ySpeed;}}class Rocket {constructor() {const quarterW = c2.width / 4;this.x = quarterW + Math.random() * (c2.width - quarterW);this.y = c2.height - 15;this.angle = Math.random() * Math.PI / 4 - Math.PI / 6;this.blastSpeed = 6 + Math.random() * 7;this.shardCount = 15 + Math.floor(Math.random() * 15);this.xSpeed = Math.sin(this.angle) * this.blastSpeed;this.ySpeed = -Math.cos(this.angle) * this.blastSpeed;this.hue = Math.floor(Math.random() * 360);this.trail = [];}draw() {ctx2.save();ctx2.translate(this.x, this.y);ctx2.rotate(Math.atan2(this.ySpeed, this.xSpeed) + Math.PI / 2);ctx2.fillStyle = `hsl(${this.hue}, 100%, 50%)`;ctx2.fillRect(0, 0, 5, 15);ctx2.restore();}update() {this.x = this.x + this.xSpeed;this.y = this.y + this.ySpeed;this.ySpeed += 0.1;}explode() {for (let i = 0; i < 70; i++) {shards.push(new Shard(this.x, this.y, this.hue));}}}// INITIALIZATION
const [c1, c2, c3] = document.querySelectorAll('canvas');
const [ctx1, ctx2, ctx3] = [c1, c2, c3].map(c => c.getContext('2d'));
let fontSize = 200;
const rockets = [];
const shards = [];
const targets = [];
const fidelity = 3;
let counter = 0;
c2.width = c3.width = window.innerWidth;
c2.height = c3.height = window.innerHeight;
ctx1.fillStyle = '#000';
const text = 'HTML5';
let textWidth = 99999999;while (textWidth > window.innerWidth) {ctx1.font = `900 ${fontSize--}px Arial`;textWidth = ctx1.measureText(text).width;
}c1.width = textWidth;
c1.height = fontSize * 1.5;
ctx1.font = `900 ${fontSize}px Arial`;
ctx1.fillText(text, 0, fontSize);
const imgData = ctx1.getImageData(0, 0, c1.width, c1.height);
for (let i = 0, max = imgData.data.length; i < max; i += 4) {const alpha = imgData.data[i + 3];const x = Math.floor(i / 4) % imgData.width;const y = Math.floor(i / 4 / imgData.width);if (alpha && x % fidelity === 0 && y % fidelity === 0) {targets.push({ x, y });}
}ctx3.fillStyle = '#FFF';
ctx3.shadowColor = '#FFF';
ctx3.shadowBlur = 25;// ANIMATION LOOP
(function loop() {ctx2.fillStyle = "rgba(0, 0, 0, .1)";ctx2.fillRect(0, 0, c2.width, c2.height);//ctx2.clearRect(0, 0, c2.width, c2.height);counter += 1;if (counter % 15 === 0) {rockets.push(new Rocket());}rockets.forEach((r, i) => {r.draw();r.update();if (r.ySpeed > 0) {r.explode();rockets.splice(i, 1);}});shards.forEach((s, i) => {s.draw();s.update();if (s.timer >= s.ttl || s.lightness >= 99) {ctx3.fillRect(s.target.x, s.target.y, fidelity + 1, fidelity + 1);shards.splice(i, 1);}});requestAnimationFrame(loop);
})();// HELPER FUNCTIONS
const lerp = (a, b, t) => Math.abs(b - a) > 0.1 ? a + t * (b - a) : b;function getTarget() {if (targets.length > 0) {const idx = Math.floor(Math.random() * targets.length);let { x, y } = targets[idx];targets.splice(idx, 1);x += c2.width / 2 - textWidth / 2;y += c2.height / 2 - fontSize / 2;return { x, y };}
}

canvas制作马赛克&飞鸟动画&小球拖拽动画

canvas制作马赛克&飞鸟动画&小球拖拽动画

canvas实现移动端刮刮卡

canvas实现移动端刮刮卡

带你使用canvas实现时钟

带你使用canvas实现时钟

后记

喜欢的小伙伴记得关注哦
更多好玩的特效等你来拿哦
在这里插入图片描述

在这里插入图片描述

这篇关于canvas曲线运动阳光下的泡沫效果泡沫曲线运动矩阵数字雨烟花效果方块旋转的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/173468

相关文章

Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)

《Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)》文章介绍了如何使用dhtmlx-gantt组件来实现公司的甘特图需求,并提供了一个简单的Vue组件示例,文章还分享了一... 目录一、首先 npm 安装插件二、创建一个vue组件三、业务页面内 引用自定义组件:四、dhtmlx

前端原生js实现拖拽排课效果实例

《前端原生js实现拖拽排课效果实例》:本文主要介绍如何实现一个简单的课程表拖拽功能,通过HTML、CSS和JavaScript的配合,我们实现了课程项的拖拽、放置和显示功能,文中通过实例代码介绍的... 目录1. 效果展示2. 效果分析2.1 关键点2.2 实现方法3. 代码实现3.1 html部分3.2

Java数字转换工具类NumberUtil的使用

《Java数字转换工具类NumberUtil的使用》NumberUtil是一个功能强大的Java工具类,用于处理数字的各种操作,包括数值运算、格式化、随机数生成和数值判断,下面就来介绍一下Number... 目录一、NumberUtil类概述二、主要功能介绍1. 数值运算2. 格式化3. 数值判断4. 随机

css实现图片旋转功能

《css实现图片旋转功能》:本文主要介绍了四种CSS变换效果:图片旋转90度、水平翻转、垂直翻转,并附带了相应的代码示例,详细内容请阅读本文,希望能对你有所帮助... 一 css实现图片旋转90度.icon{ -moz-transform:rotate(-90deg); -webkit-transfo

基于Python实现PDF动画翻页效果的阅读器

《基于Python实现PDF动画翻页效果的阅读器》在这篇博客中,我们将深入分析一个基于wxPython实现的PDF阅读器程序,该程序支持加载PDF文件并显示页面内容,同时支持页面切换动画效果,文中有详... 目录全部代码代码结构初始化 UI 界面加载 PDF 文件显示 PDF 页面页面切换动画运行效果总结主

React实现原生APP切换效果

《React实现原生APP切换效果》最近需要使用Hybrid的方式开发一个APP,交互和原生APP相似并且需要IM通信,本文给大家介绍了使用React实现原生APP切换效果,文中通过代码示例讲解的非常... 目录背景需求概览技术栈实现步骤根据 react-router-dom 文档配置好路由添加过渡动画使用

使用Python实现生命之轮Wheel of life效果

《使用Python实现生命之轮Wheeloflife效果》生命之轮Wheeloflife这一概念最初由SuccessMotivation®Institute,Inc.的创始人PaulJ.Meyer... 最近看一个生命之轮的视频,让我们珍惜时间,因为一生是有限的。使用python创建生命倒计时图表,珍惜时间

Qt QWidget实现图片旋转动画

《QtQWidget实现图片旋转动画》这篇文章主要为大家详细介绍了如何使用了Qt和QWidget实现图片旋转动画效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、效果展示二、源码分享本例程通过QGraphicsView实现svg格式图片旋转。.hpjavascript

从去中心化到智能化:Web3如何与AI共同塑造数字生态

在数字时代的演进中,Web3和人工智能(AI)正成为塑造未来互联网的两大核心力量。Web3的去中心化理念与AI的智能化技术,正相互交织,共同推动数字生态的变革。本文将探讨Web3与AI的融合如何改变数字世界,并展望这一新兴组合如何重塑我们的在线体验。 Web3的去中心化愿景 Web3代表了互联网的第三代发展,它基于去中心化的区块链技术,旨在创建一个开放、透明且用户主导的数字生态。不同于传统

usaco 1.2 Name That Number(数字字母转化)

巧妙的利用code[b[0]-'A'] 将字符ABC...Z转换为数字 需要注意的是重新开一个数组 c [ ] 存储字符串 应人为的在末尾附上 ‘ \ 0 ’ 详见代码: /*ID: who jayLANG: C++TASK: namenum*/#include<stdio.h>#include<string.h>int main(){FILE *fin = fopen (