摘要:預(yù)覽完整項目預(yù)覽預(yù)覽地址粒子效果原理在中,可以通過方法來獲取像素數(shù)據(jù)。下例是通過改變像素的數(shù)據(jù)而重新寫出來的文字。不過可能會造成文字部分地方缺失。煙花效果可以看一下我的上一篇,程序員的小浪漫煙火完整項目項目地址如果覺得還不錯,請一個吧。
預(yù)覽
完整項目預(yù)覽----預(yù)覽地址;
粒子效果原理在canvas中,可以通過getImageData()方法來獲取像素數(shù)據(jù)。
ctx.fillStyle = "#ff0000"; ctx.fillRect(0, 0, 1, 1); const imageData = ctx.getImageData(0, 0, 1, 1);
imageData有三個屬性:
data:數(shù)組,包含了像素信息,每個像素會有四個長度,如[255,0,0,255, ... ,255,127,0,255],分別代表該像素的RGBA值。
width:imageData對象的寬。
height:imageData對象的高。
首先在canvas上寫上某種顏色文字,再去分析像素數(shù)據(jù)(比如改像素是否有透明度等),然后自己記錄下該像素點的位置。
下例是通過改變像素的數(shù)據(jù)而重新寫出來的文字。
ctx.font = "bold 40px Arial"; ctx.textBaseline = "middle"; ctx.textAlign = "center"; ctx.fillText("你好啊", 60, 20); document.querySelector("#button").addEventListener("click", function(){ const imgData = ctx.getImageData(0, 0, 120, 40); for(let i = 0;i < imgData.data.length; i+=4){ if(imgData.data[i + 3] == 0) continue; imgData.data[i] = 255; imgData.data[i + 1] = 0; imgData.data[i + 2] = 0; // imgData.data[i + 3] = 255; 這個代表的是透明度 透明度不變 255最高 0最低 } ctx.putImageData(imgData,120,0); });
這段代碼只是示例說明一下,實際上才不會有人這么腦殘去換顏色吧。
獲取點位置要獲取點的位置,首先要將字寫在畫布上,但是字又不能讓別人看到。所以可以動態(tài)創(chuàng)建一個畫布,這個畫布不會append到任何節(jié)點上,只會用于寫字。
const cache = document.createElement("canvas");
將寬高等與展示的畫布設(shè)置成一樣的。(不貼這部分的代碼了)
創(chuàng)建一個對象,用于獲取點的位置
const ShapeBuilder = { //初始化字的對齊方式等,我認(rèn)為middle 與 center比較好計算一點 init(width, height){ this.width = width; this.height = height; this.ctx = cache.getContext("2d"); this.ctx.textBaseline = "middle"; this.ctx.textAlign = "center"; }, //獲取位置之前必須先要寫入文字。 這里的size=40是默認(rèn)值 write(words, x, y, size = 40){ //清除之前寫的字。 this.ctx.clearRect(0, 0, this.width, this.height); this.font = `bold ${size}px Arial`; this.ctx.fillText(words, x, y); //記錄當(dāng)前文字的位置,方便計算獲取像素的區(qū)域 this.x = x; this.y = y; this.size = size; this.length = words.length; }, getPositions(){ //因為imgData數(shù)據(jù)非常的大,所以盡可能的縮小獲取數(shù)據(jù)的范圍。 const xStart = this.x - (this.length / 2) * this.size, xEnd = this.x + (this.length / 2) * this.size, yStart = this.y - this.size / 2, yEnd = this.y + this.size / 2, //getImageData(起點x, 起點y, 寬度, 高度); data = this.ctx.getImageData(xStart, yStart, this.size * this.length, this.size).data; //間隔 (下面有介紹) const gap = 4; let positions = [], x = xStart, y = yStart; for(var i = 0;i < data.length; i += 4 * gap){ if(data[i+3] > 0){ positions.push({x, y}); } x += gap; if(x >= xEnd){ x = xStart; y += gap; i += (gap - 1) * 4 * (xEnd - xStart); } } return positions; } } ShapeBuilder.init();
關(guān)于gap:在循環(huán)imgData數(shù)組的時候,數(shù)據(jù)量太大可能會造成卡頓,所以可以使用間隔來獲取坐標(biāo)點的方法。不過可能會造成文字部分地方缺失。就需要個人來權(quán)衡利弊,自己來調(diào)整了。
gap的值必須能被xEnd-xStart給整除,不然會造成獲取坐標(biāo)點錯位的后果。
關(guān)于canvas中middle與center的規(guī)則:
this.ctx.font = "bold 40px Arial"; this.ctx.fillText("你好",40 ,20);
效果如下圖所示
fillText設(shè)置的坐標(biāo)點剛好會是整個字的中點,就是圖中middle與center的交點。其實以其它對齊方式也是可以的,看個人喜好。
更多的對齊規(guī)則參考HTML 5 Canvas 參考手冊的文本。
創(chuàng)建微粒類微粒應(yīng)該隨機(jī)生成,然后移動到指定的位置去。
微粒類的屬性:
自身當(dāng)前位置(x,y), 目標(biāo)位置:(xEnd,yEnd),自身大小(size),自身顏色(color),移動快慢(e)
方法:go():每一幀都要移動一段距離,render():渲染出微粒(我用心形的形狀)
class Particle { constructor({x, y, size = 2, color, xEnd, yEnd, e = 60} = {}){ this.x = x; this.y = y; this.size = size; this.color = color || `hsla(${Math.random() * 360}, 90%, 65%, 1)`; this.xEnd = xEnd; this.yEnd = yEnd; //經(jīng)過e幀之后到達(dá)目標(biāo)地點 this.e = e; //計算每一幀走過的距離 this.dx = (xEnd - x) / e; this.dy = (yEnd - y) / e; } go(){ //到目的后保持不動 (其實這里也可以搞點事情的) if(--this.e <= 0) { this.x = this.xEnd; this.y = this.yEnd; return ; } this.x += this.dx; this.y += this.dy; } render(ctx){ this.go(); //下面是畫出心型的貝塞爾曲線 ctx.beginPath(); ctx.fillStyle = this.color; ctx.moveTo(this.x + 0.5 * this.size, this.y + 0.3 * this.size); ctx.bezierCurveTo(this.x + 0.1 * this.size, this.y, this.x, this.y + 0.6 * this.size, this.x + 0.5 * this.size, this.y + 0.9 * this.size); ctx.bezierCurveTo(this.x + 1 * this.size, this.y + 0.6 * this.size, this.x + 0.9 * this.size, this.y, this.x + 0.5 * this.size, this.y + 0.3 * this.size); ctx.closePath(); ctx.fill(); return true; } }
微粒類最基本的屬性與方法就是這些,如果要讓粒子更好看一點,或者更生動一點,可以自己添加一些屬性與方法。
具體流程const canvas = { init(){ //設(shè)置一些屬性 this.setProperty(); //創(chuàng)建微粒 this.createParticles(); //canvas的循環(huán) this.loop(); }, setProperty(){ this.ctx = studio.getContext("2d"); this.width = document.body.clientWidth; this.height = document.body.clientHeight; this.particles = []; }, createParticles(){ let dots; //ShapeBuilder.write(words, x, y, size) ShapeBuilder.write("每個字都是",this.width / 2, this.height / 3, 120); dots = ShapeBuilder.getPositions(6); ShapeBuilder.write("愛你的模樣", this.width / 2, this.height * 2 / 3, 120); dots = dots.concat(ShapeBuilder.getPositions(6)); //dots已經(jīng)獲取到了字的坐標(biāo)點 //每一個微粒的目標(biāo)地點都是dots的坐標(biāo) //每一個微粒都隨機(jī)出生在畫布的某個位置 for(let i = 0; i < dots.length; i++){ this.particles.push(new Particle({ xEnd:dots[i].x, yEnd:dots[i].y , x: Math.random() * this.width, y: Math.random() * this.height, size:6, color:"hsla(360, 90%, 65%, 1)" })); } }, loop(){ //每一幀清除畫布,然后再渲染微粒就可以了 requestAnimationFrame(this.loop.bind(this)); this.ctx.clearRect(0, 0, this.width, this.height); for(var i = 0; i < this.particles.length; i++){ this.particles[i].render(this.ctx); } } } canvas.init();
如果想要給每個粒子加上小尾巴的話,那么在每一幀的時候,就不要清除畫布,而且覆蓋一層有透明度的底色。
//修改loop方法 //this.ctx.clearRect(0, 0, this.width, this.height); this.ctx.fillStyle = "rgba(0,0,0,0.2)"; this.ctx.fillRect(0, 0, this.width, this.height);
這樣的話會變成如下效果
在這這篇文章的時候,并沒有注意太多細(xì)節(jié),比如gap應(yīng)該是可以被設(shè)置的,或者是一個被特殊標(biāo)注的常量,而不應(yīng)該隨便寫在方法中。對于本例的代碼,切勿生搬硬套,重要的是要理解原理,以及自己親自動手嘗試。
我也是在寫這篇文章的過程中,才發(fā)現(xiàn)了之前獲取position一個不精準(zhǔn)的地方。
這里只講了粒子效果最基礎(chǔ)的用法,實際上還可以做出很多非常炫酷的效果
比如在粒子到達(dá)目的地后還可以抖動什么的
粒子形狀、顏色的變化等等。
這個項目還可以搞很多事情的,大家也可以自己多來嘗試弄些更加炫酷的效果。
煙花效果可以看一下我的上一篇,程序員的小浪漫----煙火
完整項目github項目地址
如果覺得還不錯,請star一個吧。
參考項目github上的一個項目---- shape-shifter
這個項目我覺得非常不錯,可惜作者都消失好多年了。
codepen.io 上的一個作品 ---- Love In Hearts
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/92920.html
摘要:多代碼,慎讀預(yù)覽完整項目預(yù)覽預(yù)覽地址屬性設(shè)計煙花狀態(tài)煙花應(yīng)有三個狀態(tài)升空等待炸裂炸裂后煙花發(fā)射點,爆炸點,升空后等待炸裂時間,炸裂后微粒個數(shù),煙花半徑煙花炸裂后微粒自身位置,自身大小,自身速度,最大煙花半徑。 多代碼,慎讀!!! 預(yù)覽 showImg(https://segmentfault.com/img/remote/1460000013324854?w=349&h=423); 完...
摘要:代碼實現(xiàn)炫麗的粒子運(yùn)動效果云庫前端散開類型歸位隨機(jī)散開效果對歸位有效輸入漢字后回車代碼不多,只要是幾個操作元素。看起來運(yùn)行順暢的代碼也或多或少有一些瑕疵,日前這個效果還只支持中文。 沒有最好,只有更好,如題所示,這篇文章只要是分享一個用 Canvas 來實現(xiàn)的粒子運(yùn)動效果。感覺有點標(biāo)題黨了,但換個角度,勉勉強(qiáng)強(qiáng)算是炫麗吧,雖然色彩上與炫麗無關(guān),但運(yùn)動效果上還是算得上有點點炫的。不管怎么...
摘要:代碼文件每周一點動畫系列文章目前已經(jīng)更新了篇,今天給大家發(fā)個福利。粒子的位置為,我們作為參數(shù)傳入。粒子切換粒子切換的代碼在中,很簡單,就是綁定了兩個事件。 代碼文件 每周一點canvas動畫系列文章目前已經(jīng)更新了12篇,今天給大家發(fā)個福利。我們使用canvas來制作一個小的效果。這個小效果是我從codePen上看到的,我對其做了些修改增強(qiáng),添加了一些新的功能。UI界面就如下圖中看到的樣...
閱讀 3094·2021-08-03 14:05
閱讀 2140·2019-08-29 15:35
閱讀 678·2019-08-29 13:30
閱讀 3169·2019-08-29 13:20
閱讀 2531·2019-08-23 18:15
閱讀 1797·2019-08-23 14:57
閱讀 2213·2019-08-23 13:57
閱讀 1310·2019-08-23 12:10