原生js实现canvas气泡冒泡效果
生活随笔
收集整理的這篇文章主要介紹了
原生js实现canvas气泡冒泡效果
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
說明:?
本文章主要分為ES5和ES6兩個版本
ES5版本是早期版本,后面用ES6重寫優化的,建議使用ES6版本。
?
1, 原生js實現canvas氣泡冒泡效果的插件,api豐富,使用簡單
2, 只需引入JumpBubble.js一個js文件即可
?
項目源碼地址:?https://github.com/roonly/JumpBubble
== 使用demo:
?
ES6版本的使用demo:
const bubble = new JumpBubble(document.getElementById(`cavs`));
bubble.create('/img/fish.png');
?
ES5代碼的時候demo:
var demo = new JumpBubble({
? elCanvas : document.getElementById("canvasIdName")
});
demo.create({
? elImg : document.getElementById("imgIdName")
});
?
== 效果:
?
== html 代碼:
?
ES6版本:
//html內容<canvas id="cavs" width="130" height="400" style="border:1px solid #fff;">您的瀏覽器不支持canvas標簽~</canvas>//index.js內容: let list = ['http://p4.cdn.btime.com/t01e430315c854b44d2.png','http://p5.qhimg.com/t017f9904d4be818a87.png','http://p5.qhimg.com/t015ec16e404a442dd4.png','/img/fish.png', //注:路徑是相對html的路徑,因為該路徑最終會放到img標簽的src上 ]; const bubble = new JumpBubble(document.getElementById(`cavs`)); setInterval(() => {if(s > list.length - 1){s = 0;}bubble.create(list[s]);s++ },250);
?
ES5版本:
<!DOCTYPE HTML> <html> <meta charset="utf-8"> <meta name="renderer" content="webkit"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title>原生js實現canvas氣泡冒泡效果</title> <body> <p style="display:none"><img id="img1" src="http://p8.qhimg.com/t01053ab4d4d6510abd.png" alt=""><img id="img2" src="http://p5.qhimg.com/t017f9904d4be818a87.png" alt=""><img id="img3" src="http://p5.qhimg.com/t015ec16e404a442dd4.png" alt=""><img id="img4" src="http://p6.qhimg.com/t017895dcd6312beacb.png" alt=""><img id="img5" src="http://p2.qhimg.com/t01f70bccf10e16addd.png" alt=""><img id="img6" src="http://p3.qhimg.com/t016d419cab67d819ac.png" alt=""> </p> <h2>小賊說:原生js實現canvas氣泡冒泡效果的插件,api豐富,使用簡單</h2> <canvas id="myCanvas" width="250" height="430" style="border:1px solid #eee">您的瀏覽器不支持canvas標簽~</canvas> <canvas id="myCanvas2" width="200" height="330" style="border:1px solid #eee">您的瀏覽器不支持canvas標簽~</canvas> <script src="JumpBubble.js"></script> <script type="text/javascript"> window.onload = function(){// 使用demovar demo = new JumpBubble({elCanvas : document.getElementById("myCanvas")});clearInterval(setDemo1);var setDemo1 = setInterval(function(){var idName = "img" + Math.ceil(Math.random()*6);demo.create({elImg : document.getElementById(idName)});},150);// demo2var demo2 = new JumpBubble({elCanvas : document.getElementById("myCanvas2"),config : {alpha : 0.5,width : 30},callback : function(a,b,c){}});clearInterval(setDemo2);var setDemo2 = setInterval(function(){demo2.create({elImg : document.getElementById("img1")});},150);}</script> </body> </html>?
?
== javascript 代碼:(分為ES6版 和 ES5版)
?
ES6版:
?
1 /* 2 ** @param canvasNode [必傳] <DOM> canvan標簽元素 3 ** @param config [選傳] <Obj> 可選配置項 4 ** - effect <Str> 氣泡的y軸浮動效果,可選2個值:linear、ease,默認'ease' 5 ** - speed <Str> 氣泡的Y軸移動速度,可選3個值 slow default fast, 默認'default' 6 ** - isToAlapha <Boo> 氣泡是否逐漸增加透明度 默認true 7 ** - alpha <Num> 初始氣泡的透明度, 默認0.9 8 ** - max <Num> //畫布上最多同時存在多少氣泡,默認30,請根據氣泡出現頻道調整此值,已達到最優的視覺效果 9 ** - diffWidth <Num> 初始冒泡時氣泡寬度與指定寬度(width屬性值)的差值,默認15,差值越大,冒泡時氣泡從小變大的效果越明顯 10 ** - left <Num> 冒泡位置,距離畫布左側像素數,默認在畫布中間靠左15像素位置 11 ** - top <Num> 冒泡位置,距離畫布頂部像素數,默認距離畫布頂部為畫布高度減30像素 12 ** - width <Num> 氣泡的寬度, 默認30,為保證氣泡不變形,高度隨寬度改變 13 ** @param callback [選傳] <Func> 態度氣泡實例初始化后的回調 14 */ 15 export default class JumpBubble{ 16 constructor(canvasNode, config = {}, callback ){ 17 if(!canvasNode || !canvasNode.getContext){ 18 console.warn("jumpBuffle,啟用失敗,canvas傳參錯誤 或 瀏覽器不支持canvas"); 19 this.error = true; 20 return false; 21 } 22 const t = this, 23 { width, height } = canvasNode; 24 const _config = { //配置氣泡冒泡設置 25 width: 30, //可自定義氣泡寬度,高度隨寬度變化 26 left : width/2 - 15, //距離左側距離 27 top : height - 30, //距離頂部距離 28 alpha : 0.9, // 透明度設置 29 effect: 'ease', 30 speed: 'default', 31 max: 30, //畫布上最多同時存在多少氣泡, 默認30 32 isToAlapha: true, 33 diffWidth: 15, // 初始冒泡時氣泡寬度與正常寬度的差值,默認15 34 cavHeight: height, // [非配置項] canvan標簽的高度,設置氣泡在不同高度有不同的浮動速度時會用到 35 cavWidth: width, // [非配置項] canvan標簽的高度,設置氣泡在左右晃動,觸壁反彈時會用到 36 }; 37 Object.assign(t, { 38 canvasInfo: { 39 canvas : canvasNode, 40 width, 41 height 42 }, 43 config: Object.assign(_config, config), 44 ctx: canvasNode.getContext("2d"), 45 bubbleArr: [],//用來存儲所有的氣泡 46 allImg: { //緩存創建的img標簽 47 lists: [], //src的列表 48 doms: [], //緩存list對應的創建的dom 49 loadState: [], //是否img標簽已經加載完畢,如果完畢再直接返回img標簽的dom,如果沒有加載完畢則放到img.load函數內返回imgdom 50 } 51 }); 52 callback && callback(t); 53 } 54 /* 55 ** 冒泡的生命周期 56 ** before: 氣泡開始冒泡前 57 ** after: 單個氣泡消失后 58 */ 59 create(imgsrc, before, after){ 60 const t = this, 61 {ctx, error} = t; 62 if(!ctx || error){ 63 console.warn("jumpBuffle:create時,ctx錯誤"); 64 return false; 65 } 66 const { bubbleArr, canvasInfo } = t, 67 { width: imgwidth, max } = t.config; 68 t.createImg(imgsrc).then(imgNode => { 69 const imgInfo = { 70 el : imgNode, 71 width : imgwidth || imgNode.width, 72 height : imgwidth && imgNode.height*(imgwidth/imgNode.width) || imgNode.height 73 }; 74 if(bubbleArr.length > max){ 75 return; 76 } 77 bubbleArr.push(new DrawImg(ctx, imgInfo, t.config)); 78 //每添加一個氣泡觸發一次的回調函數, 生命周期為氣泡開始冒泡前 79 before && before(canvasInfo.canvas, imgNode, bubbleArr); 80 if(!t.setInter){ 81 t.setInterFn(after); 82 } 83 }); 84 return this; 85 } 86 createImg(imgsrc){ 87 return new Promise(res => { 88 const { lists, doms, loadState } = this.allImg; 89 const i = lists.indexOf(imgsrc); 90 if(i > -1 && loadState[i]){ 91 res(doms[i]); 92 return ; 93 } 94 const img = document.createElement('img'); 95 img.src = imgsrc; 96 img.setAttribute('style', 'display:none;'); 97 document.body.appendChild(img); 98 lists.push(imgsrc); 99 doms.push(img); 100 img.onload = () => { 101 loadState.push(true); 102 res(img); 103 } 104 }) 105 } 106 setInterFn(after){ 107 const t = this, 108 { ctx, canvasInfo } = t, 109 { width, height } = canvasInfo; 110 t.setInter = setInterval(function(){ 111 try{ 112 ctx.clearRect(0, 0, width, height); 113 t.bubbleArr = t.bubbleArr.filter(function(val){ 114 val.addCtx(); 115 val.updateCtx(); 116 if(val.y < 10){ 117 after && after(); 118 return false; 119 }else{ 120 return true; 121 } 122 }); 123 if(t.bubbleArr.length === 0){ 124 clearInterval(t.setInter); 125 t.setInter = null; 126 ctx.clearRect(0, 0, width, height); 127 } 128 }catch(e){ 129 console.warn('創建態度氣泡出錯',e); 130 clearInterval(t.setInter); 131 } 132 },25); 133 } 134 } 135 136 class DrawImg{ 137 constructor(ctx, imgInfo, { left, top, alpha, speed, cavWidth, cavHeight, effect, isToAlapha, diffWidth }){ 138 Object.assign(this, { 139 whichUnit: null, // 標識氣泡在畫布中的位置 ,canvan畫布分成3個部分,2:中上、1:中、0:中下 140 ctx, 141 originWidth: imgInfo.width, 142 img: imgInfo.el, 143 imgWidth: imgInfo.width - diffWidth, //氣泡初始大小與指定氣泡大小的差值 144 imgHeight: imgInfo.height - diffWidth, 145 x: left, //氣泡在x軸的位置 相對左側 146 y: top, //氣泡在y軸位置 相對頂部 147 alpha, 148 speed, 149 effect, 150 isToAlapha, 151 cavWidth, 152 oneUnit: cavHeight/4, //將canvan畫布分成3個部分,中上、中、中下(中下占2/4,其他各占1/4) 153 toRight: (Math.random() > 0.5 ? false : true), //在氣泡左右晃動效果時,該屬性標識氣泡是向左晃動還是向右晃動。 154 xPx: Math.random()*2.5, //x軸氣泡每次位移像素數 155 yPx: null, //緩存氣泡在畫布y軸上每次位移的距離 156 yPxArr: null, //緩存y軸每次位移像素數的3個階段的值的數組 157 diffAlapa: null, //緩存氣泡在變為透明時每次增加的透明度 158 }); 159 this.updateCtx = this.updateCtx.bind(this); 160 this.effectCommon = this.effectCommon.bind(this); 161 } 162 getSpeed(type = 'default'){ 163 switch(type){ 164 case 'slow': 165 return 0.6; 166 case 'fast': 167 return 1.4; 168 default: 169 return 1; 170 } 171 } 172 addCtx(){ 173 const p = this, 174 { ctx } = p; 175 ctx.save(); 176 ctx.globalAlpha = p.alpha; 177 ctx.drawImage(p.img, p.x, p.y, p.imgWidth, p.imgHeight); 178 ctx.restore(); 179 } 180 setImgWidth(){ 181 const { originWidth, imgWidth } = this; 182 if(imgWidth < originWidth){//差值diffWidth的初始小氣泡,逐漸變大為指定氣泡的大小 183 this.imgWidth += 1; 184 this.imgHeight += 1; 185 } 186 } 187 setAlapa(){ 188 const p = this, 189 { y, isToAlapha, whichUnit } = p; 190 if(!isToAlapha)return false; //可自行配置,是否逐漸增加透明度 191 let diffAlapa = p.diffAlapa; 192 if(whichUnit === 2){ //氣泡在畫布中上部分時 193 if(!diffAlapa){ 194 p.diffAlapa = diffAlapa = p.countDiffAlapa(); 195 } 196 if(p.alpha <= diffAlapa){ 197 p.alpha = 0; 198 }else{ 199 p.alpha -= diffAlapa; 200 } 201 } 202 } 203 countDiffAlapa(){ 204 const { alpha, oneUnit, yPx } = this; 205 return (alpha + 0.1)/(oneUnit/yPx); 206 } 207 setYpx(){ 208 const p = this, 209 { y, oneUnit } = p; 210 let i; 211 // 根據態度氣泡在容器內到達高度不同,設置不同的速度 212 switch(true){ 213 case y < oneUnit: //氣泡在畫布中上部分時 214 i = 2; 215 break; 216 case y > oneUnit && y < oneUnit*2: //氣泡在畫布的中部分時 217 i = 1; 218 break; 219 default: //氣泡在畫布的中下部分 220 i = 0; 221 } 222 if(p.whichUnit !== i){ 223 p.yPx = p.yPxArr[i]; 224 p.whichUnit = i; 225 } 226 p.y -= p.yPxArr[i]; 227 } 228 setYpxArr(){ 229 if(this.yPxArr)return false; 230 this.yPxArr = this.countYpxArr(this.effect, this.getSpeed(this.speed)); 231 } 232 countYpxArr(effect, speedNum = 1){//根據effect不同,設置氣泡在y軸上位移距離,數組包含3個值,分別表示在y軸的3個階段的位移距離 233 const basePx = 2; 234 const easeArr = [basePx-0.5, basePx, basePx+0.5]; 235 const linearArr = [basePx, basePx, basePx]; 236 switch(effect){ 237 case 'ease': 238 return mlt(easeArr); 239 case 'linear': 240 return mlt(linearArr); 241 default: 242 return mlt(easeArr); 243 } 244 function mlt(arr){ 245 return arr.map(v => v*speedNum); 246 } 247 } 248 pullback(){//在x軸上,觸壁反彈效果,晃動的上升, 249 const p = this, 250 { y, cavWidth, xPx, originWidth } = p; 251 // 控制氣泡左右晃動,觸壁反彈效果 252 switch(true){ 253 case (p.x + originWidth >= cavWidth): //氣泡觸右側壁 254 p.toRight = false; 255 p.x -= xPx; 256 break; 257 case p.x <= 2: //氣泡觸左側壁 258 p.toRight = true; 259 p.x += xPx; 260 break; 261 case p.toRight: 262 p.x += xPx; 263 break; 264 default: 265 p.x -= xPx; 266 } 267 this.effectCommon(); 268 } 269 effectCommon(){ 270 this.setYpxArr(); 271 this.setYpx(); 272 this.setAlapa(); 273 this.setImgWidth(); 274 } 275 updateCtx(){ 276 this.pullback(); 277 } 278 }?
?
ES5版:
?
;(function(window){function JumpBubble(opt){var t = this,canvas = opt.elCanvas,canvasW = canvas.width,canvasH = canvas.height;if(!canvas){console.warn("jumpBuffle:new 實例時,canvas傳參錯誤");return;}t.canvasInfo = {canvas : canvas,width : canvasW,height : canvasH};var canvas = t.canvasInfo.canvas;if(!canvas.getContext){console.warn("jumpBuffle,啟用失敗,瀏覽器不支持canvas");return;}var config = { //配置氣泡冒泡設置left : canvasW/2 - 15, //距離左側距離top : canvasH - 30, //距離頂部距離alpha : 0.9 // 透明度設置// width : 30 // 默認使用傳入圖片的實際寬高,可自定義氣泡寬度,高度隨寬度變化};t.callback = opt.callback; //每添加一個氣泡觸發一次的回調函數t.config = hrExtend(config,opt.config);t.ctx = canvas.getContext("2d");t.bubbleArr = []; //用來存儲所有的氣泡};JumpBubble.prototype.create = function(opt){var t = this,bubbleArr = t.bubbleArr,ctx = t.ctx,img = opt.elImg,config = t.config,cfgImgWidth = config.width,convasInfo = t.canvasInfo,callback = t.callback;if(!ctx){console.warn("jumpBuffle:create時,ctx錯誤");return;}var imgInfo = {el : img,width : cfgImgWidth || img.width,height : cfgImgWidth && img.height*(cfgImgWidth/img.width) || img.height};if(bubbleArr.length>30){return false;}bubbleArr.push(new drawImg(ctx,imgInfo,t.config,convasInfo));//每添加一個氣泡觸發一次的回調函數,// 參數1:canvas元素;參數2:傳入的圖片元素;參數3:當前存在的氣泡數組callback && callback(convasInfo.canvas,img,bubbleArr); if(!t.setInter){t.setInterFn();}};JumpBubble.prototype.setInterFn = function(){var t = this,ctx = t.ctx,convasInfo = t.canvasInfo,canvasW = convasInfo.width,canvasH = convasInfo.height;t.setInter = setInterval(function(){ctx.clearRect(0,0,canvasW,canvasH);t.bubbleArr = t.bubbleArr.filter(function(val){val.addCtx();val.updateCtx();if(val.y < 10){return false;}else{return true;}});if(t.bubbleArr.length === 0){clearInterval(t.setInter);t.setInter = null;ctx.clearRect(0,0,canvasW,canvasH);}},25);};function drawImg(ctx,imgInfo,config ,canvasInfo){var p = this;p.ctx = ctx;p.imgInfo = imgInfo,p.img = imgInfo.el;p.imgWidth = imgInfo.width - 10;p.imgHeight = imgInfo.height - 10;p.x = config.left;p.y = config.top;p.alpha = config.alpha;p.canvasInfo = canvasInfo;p.ranX = (Math.random()*5 - 2.5)/2;}drawImg.prototype.addCtx = function(){var p = this,ctx = p.ctx;ctx.save();ctx.globalAlpha = p.alpha;ctx.drawImage(p.img,p.x,p.y,p.imgWidth, p.imgHeight);ctx.restore();}drawImg.prototype.updateCtx = function(){var p = this,canvasInfo = p.canvasInfo,afterRoad = canvasInfo.height/4,ranX = p.ranX;if(p.y < afterRoad){if(Math.random() > 0.5){p.x += ranX/2;}p.y -= 2.5;if(p.alpha <= 0.02){p.alpha = 0;}else{p.alpha -= 0.02;}}else if(p.y > afterRoad && p.y < afterRoad*2){p.x += ranX/2;p.y -= 3;p.alpha -= 0.01;}else{p.x += ranX;p.y -= 4;}if(p.imgWidth < p.imgInfo.width){p.imgWidth += 1;p.imgHeight += 1;}}function deepCopy(p,c){/*@param p [必選] [對象] 被克隆對象**c :[可選] p對象被克隆到c身上,c被改變**返回值為深度克隆后的c*/var c= c || {};for(var i in p){if (typeof p[i] === 'object') {c[i] = (p[i].constructor === Array) ? [] : {};arguments.callee(p[i],c[i]);} else {c[i] = p[i];}}return c;};// 至少傳入2個參數,傳入的參數都將會被深度復制,不會影響原對象,然后返回擴展后的新對象function hrExtend() { //擴展對象var args = arguments;if (args.length < 2) return;var temp = deepCopy(args[0]); //調用復制對象方法for (var n = 1; n < args.length; n++) {for (var i in args[n]) {temp[i] = args[n][i];}}return temp;}window.JumpBubble = JumpBubble; })(window);?
?
?
轉載于:https://www.cnblogs.com/ronffy/p/canvas-bubble.html
總結
以上是生活随笔為你收集整理的原生js实现canvas气泡冒泡效果的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android--------WebVi
- 下一篇: codewars??? Is my fr