第一次用canvas繪圖,其實難度是挺大的,基於js和一些數學知識,還有參考了網上不少的例子,最終完成了demo。css
效果以下:html
![paper.gif-909.1kB][1] [1]: http://static.zybuluo.com/juanmao/bn8f0lluesbzqadn5tidi14y/paper.gifgit
調用代碼github
<html>
<head>
<meta charset="utf-8">
<title>繽紛紙片</title>
<style>
body{
background:url("img/background.jpg") no-repeat top;
}
</style>
</head>
<body>
<div id="app"></div>
<script src="./index.js"></script>
<script>
var a = new Paper({
param:{
width: 1024,//背景圖的寬
height: 1344, //背景圖的高
}
})
</script>
</body>
</html>
複製代碼
好了,接下來說解一下簡單的實現原理 首先,先定義一些咱們會用到的變量,和2個主要的對象。npm
/**
* 設計思路:2個主要方法,Paper(),Process();
* Paper()方法主要用於
* 1.建立canvas實例,build();
* 2.渲染canvas實例,render();
*
* Process()方法主要是用於:計算進程,控制動畫的狀態。
* 我以前寫過2種方式去繪製紙片,
* 之前的是每一次都從新繪製紙片的樣式,如今我把每個紙片的樣式都提早繪製好,
* 放在一個sprites的數組裏,之後的每一次渲染都不會從新繪製單個紙片了,只是回去改變這個紙片的位置和旋轉角度
*/
class Paper{
constructor(){
//定義一些默認的屬性,後面會給出口子去修改
this.CONST ={
SPRITE_WIDTH: 120, //紙片寬
SPRITE_HEIGHT: 120, //紙片高
PAPER_LENGTH: 5,//紙片數量
DURATION: 8000,
TRANSLATE_RATE: 50, //橫行平移係數
COLORS: [ //紙片的色彩值
"#EF5350","#EC407A","#AB47BC","#7E57C2",
"#5C6BC0","#42A5F5","#29B6F6","#26C6DA",
"#26A69A", "#66BB6A", "#9CCC65", "#D4E157",
"#FFEE58", "#FFCA28", "#FFA726", "#FF7043",
"#8D6E63", "#BDBDBD", "#78909C"]
}
}
}
複製代碼
接下來把一些要用的變量所有定義好canvas
class Paper{
constructor({params}){
// {...}
//定義出須要用到的一些基礎變量,還有傳進來的一些參數
//定義父元素,最基礎canvas寬高,紙片數量,定位時y的範圍,間隔時常,旋轉角度,旋轉速度
const { elm,width,height,length,yRange,duration,rotationRange,speedRange} = params
this.parent = document.getElementById(elm) || document.body;
//刪除已有的canvas
if(document.getElementsByTagName("canvas").length >0){
this.canvas = null;
this.parent.removeChild(parent.childNodes[0])
}
this.canvas = document.createElement("canvas");
this.ctx = this.canvas.getContext("2d");
this.width = width || this.parent.offsetWidth;
this.height = height || this.parent.offsetHeight;
this.length = length || this.CONST.PAPER_LENGTH;
this.yRange = yRange || this.height * 2;
//建立progress實例,將Progress的屬性繼承到Paper中的progress屬性上。
this.progress = new Progress({
duration: duration||this.CONST.DURATION,
isLoop: true
});
//旋轉角度
this.rotationRange = typeof rotationRange === "number" ? rotationRange : 0;
//旋轉速度
this.speedRange = typeof speedRange === "number" ? speedRange : 1;
//單個紙片canvas集合
this.sprites = [];
//設置最大canvas的樣式
this.canvas.style.cssText = ["display: block", "position: absolute", "top: 0", "left: 0", "pointer-events: none"].join(";");
//在頁面上渲染出來
this.parent.append(this.canvas);
}
}
複製代碼
開始定義Paper裏2個主要的方法:build(),render()數組
build(){
for(let i=0; i<this.length;++i){ //循環要建立的紙片數量
//生成每個紙片的每個小的cnavas
let canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d");
canvas.width = this.CONST.SPRITE_WIDTH; //定義的常量紙片的寬
canvas.height = this.CONST.SPRITE_HEIGHT;//定義的常量紙片的高
//定義基本的位置
canvas.position = {
initX: Math.random() * this.width,
initY: -canvas.height - Math.random() * this.yRange
};
canvas.rotation = this.rotationRange / 2 - Math.random() * this.rotationRange;
canvas.speed = this.speedRange / 2 + Math.random() * (this.speedRange / 2);
ctx.save();
//隨機的填充顏色
ctx.fillStyle = this.CONST.COLORS[Math.random() * this.CONST.COLORS.length | 0]; //隨機數判斷:圓形<1 ,四邊形<2,剩下的生成圓形
let random = Math.random()*3
let random = 2
if(random <1){
ctx.arc(10, 10, 10, 0,Math.PI*2);
}else if(random < 2){
ctx.fillRect(0, 0, canvas.width, canvas.height);
}else{
ctx.moveTo(0,0);
ctx.lineTo(0,20);
ctx.lineTo(20,20);
ctx.closePath()
}
ctx.fill();
ctx.restore();
this.sprites.push(canvas);
}
}
複製代碼
render(){
//核心代碼
for(let i = 0; i < this.length; ++i){
this.ctx.save();
/**
* 紙片的初始位置x + 紙片旋轉 * 常量平移*進程
*/
this.ctx.translate(
this.sprites[i].position.initX + this.sprites[i].rotation * this.CONST.TRANSLATE_RATE * progress, //添加到水平座標(x)上的值
this.sprites[i].position.initY + progress * (this.height + this.yRange));//添加到垂直座標(y)上的值。
this.ctx.rotate(this.sprites[i].rotation); //方法旋轉當前的繪圖。
this.ctx.drawImage(
this.sprites[i], //圖像,畫布或視頻
-this.CONST.SPRITE_WIDTH * Math.abs(Math.sin(progress * Math.PI * 2 * this.sprites[i].speed)) / 2, //在畫布上放置圖像的 x 座標位置。
//雪碧圖的width *
-this.CONST.SPRITE_HEIGHT / 2,//在畫布上放置圖像的 y 座標位置。
this.CONST.SPRITE_WIDTH * Math.abs(Math.sin(progress * Math.PI * 2 * this.sprites[i].speed)), //可選。要使用的圖像的寬度(伸展或縮小圖像)
this.CONST.SPRITE_HEIGHT); //可選。要使用的圖像的高度(伸展或縮小圖像)。
this.ctx.restore();
}
}
複製代碼
接下來咱們要定義另一個主要的對象Progressbash
class Progress{
constructor(param){
//定義默認時常,是否重複動畫
const {duration,isLoop} = param
this.timestamp = null;
this.duration = duration || 1000;
this.progress = 0;
this.delta = 0;
this.progress = 0;
this.isLoop = !!isLoop;
}
}
複製代碼
這個對象裏有3個核心的方法app
//重置時間戳
rest(){
this.timestamp = null;
}
複製代碼
//記錄從新開始的時間戳
start(now){
this.timestamp = now;
}
複製代碼
tick(now){
if (this.timestamp) {
this.delta = now - this.timestamp;
this.progress = Math.min(this.delta / this.duration, 1); //取最小值
if (this.progress >= 1 && this.isLoop) {
this.start(now);
}
return this.progress;
} else {
return 0;
}
}
複製代碼
至此,已貼出了部分的核心代碼,稍晚我會將所有的代碼貼到個人github上,喜歡的小夥伴能夠幫忙點個star~有錯誤的寫法還須要小夥伴多多指出!謝謝~dom
最新更新 我已把這個項目弄在了npm上,你們能夠去下載下來看到源碼,也能夠在項目中去調用。
npm i bling-paper
複製代碼