摘要:最近聽女神說想談戀愛了,嘿嘿,一定不能放過這個(gè)機(jī)會(huì),給她來個(gè)不一樣的表白。我們只需要判斷不為就可以得到需要的字體形狀數(shù)據(jù)了。能不能再給力一點(diǎn)說好的粒子系統(tǒng),現(xiàn)在只是簡(jiǎn)單的畫了一點(diǎn)。還有什么好玩的上面是將粒子擺成文字。
最近聽女神說想談戀愛了,?(? ? ?) 嘿嘿,一定不能放過這個(gè)機(jī)會(huì),給她來個(gè)不一樣的表白。
那么咱們就一起來把這個(gè)粒子系統(tǒng)玩出花來吧
演示地址:
https://es2049.studio/work-sh...
如何將一系列的粒子組成一句表白呢?
實(shí)現(xiàn)原理其實(shí)很簡(jiǎn)單,Canvas 中有個(gè) getImageData 的方法,可以得到一個(gè)矩形范圍所有像素點(diǎn)數(shù)據(jù)。那么我們就試試來獲取一個(gè)文字的形狀吧。
第一步,用 measureText 的方法來計(jì)算出文字適當(dāng)?shù)某叽绾臀恢谩?/p>
// 創(chuàng)建一個(gè)跟畫布等比例的 canvas const width = 100; const height = ~~(width * this.height / this.width); // this.width , this.height 說整個(gè)畫布的尺寸 const offscreenCanvas =document.createElement("canvas"); const offscreenCanvasCtx =offscreenCanvas.getContext("2d"); offscreenCanvas.setAttribute("width", width); offscreenCanvas.setAttribute("height", height); // 在這離屏 canvas 中將我們想要的文字 textAll 繪制出來后,再計(jì)算它合適的尺寸 offscreenCanvasCtx.fillStyle = "#000"; offscreenCanvasCtx.font = "bold 10px Arial"; constmeasure=offscreenCanvasCtx.measureText(textAll); // 測(cè)量文字,用來獲取寬度 const size = 0.8; // 寬高分別達(dá)到屏幕0.8時(shí)的size const fSize=Math.min(height * size * 10 / lineHeight, width * size * 10 / measure.width); // 10像素字體行高 lineHeight=7 magic offscreenCanvasCtx.font = `bold ${fSize}px Arial`; // 根據(jù)計(jì)算后的字體大小,在將文字?jǐn)[放到適合的位置,文字的坐標(biāo)起始位置在左下方 const measureResize =offscreenCanvasCtx.measureText(textAll); // 文字起始位置在左下方 let left = (width - measureResize.width) / 2; const bottom=(height + fSize / 10 * lineHeight) / 2; offscreenCanvasCtx.fillText(textAll, left, bottom);
咱們可以 appendChild 到 body 里看眼
同學(xué)們注意,我要開始變形了 [推眼鏡] 。
getImageData 獲取的像素?cái)?shù)據(jù)是一個(gè) Uint8ClampedArray (值是 0 - 255 的數(shù)組),4 個(gè)數(shù)一組分別對(duì)應(yīng)一個(gè)像素點(diǎn)的 R G B A 值。我們只需要判斷 i * 4 + 3 不為 0 就可以得到需要的字體形狀數(shù)據(jù)了。
// texts 所有的單詞分別獲取 data ,上文的 textAll 是 texts 加一起 Object.values(texts).forEach(item => { offscreenCanvasCtx.clearRect(0, 0, width, height); offscreenCanvasCtx.fillText(item.text, left, bottom); left += offscreenCanvasCtx.measureText(item.text).width; const data = offscreenCanvasCtx.getImageData(0, 0, width, height); const points = []; // 判斷第 i * 4 + 3 位是否為0,獲得相對(duì)的 x,y 坐標(biāo)(使用時(shí)需乘畫布的實(shí)際長(zhǎng)寬, y 坐標(biāo)也需要取反向) for (let i = 0, max = data.width * data.height; i < max; i++) { if (data.data[i * 4 + 3]) { points.push({ x: (i % data.width) / data.width, y: (i / data.width) / data.height }); } } // 保存到一個(gè)對(duì)象,用于后面的繪制 geometry.push({ color: item.hsla, points }); })
制定場(chǎng)景,繪制圖形
文字圖形的獲取方式以及搞定了,那么咱們就可以把內(nèi)容整體輸出了。咱們定義一個(gè)簡(jiǎn)單的腳本格式。
// hsla 格式方便以后做色彩變化的擴(kuò)展 const color1 = {h:197,s:"100%",l:"50%",a:"80%"}; const color2 = {h:197,s:"100%",l:"50%",a:"80%"}; // lifeTime 禎數(shù) const Actions = [ {lifeTime:60,text:[{text:3,hsla:color1}]}, {lifeTime:60,text:[{text:2,hsla:color1}]}, {lifeTime:60,text:[{text:1,hsla:color1}], {lifeTime:120,text:[ {text:"I",hsla:color1}, {text:"??",hsla:color2}, {text:"Y",hsla:color1}, {text:"O",hsla:color1}, {text:"U",hsla:color1} ]}, ];
根據(jù)預(yù)設(shè)的腳本解析出每個(gè)場(chǎng)景的圖形,加一個(gè) tick 判斷是否到了 lifeTime 切換到下一個(gè)圖形重新繪制圖形。
function draw() { this.tick++; if (this.tick >= this.actions[this.actionIndex].lifeTime) { this.nextAction(); } this.clear(); this.renderParticles(); // 繪制點(diǎn) this.raf = requestAnimationFrame(this.draw); } function nextAction() { ....//切換場(chǎng)景 balabala.. this.setParticle(); // 隨機(jī)將點(diǎn)設(shè)置到之前得到的 action.geometry.points 上 }
這樣咱們基本的功能已經(jīng)完成了。
能不能再給力一點(diǎn)
說好的粒子系統(tǒng),現(xiàn)在只是 context.arc 簡(jiǎn)單的畫了一點(diǎn)。那咱們就來加個(gè)粒子系統(tǒng)吧。
class PARTICLE { // x,y,z 為當(dāng)前的坐標(biāo),vx,vy,vz 則是3個(gè)方向的速度 constructor(center) { this.center = center; this.x = 0; this.y = 0; this.z = 0; this.vx = 0; this.vy = 0; this.vz = 0; } // 設(shè)置這些粒子需要運(yùn)動(dòng)到的終點(diǎn)(下一個(gè)位置) setAxis(axis) { this.nextX = axis.x; this.nextY = axis.y; this.nextZ = axis.z; this.color = axis.color; } step() { // 彈力模型 距離目標(biāo)越遠(yuǎn)速度越快 this.vx += (this.nextX - this.x) * SPRING; this.vy += (this.nextY - this.y) * SPRING; this.vz += (this.nextZ - this.z) * SPRING; // 摩擦系數(shù) 讓粒子可以趨向穩(wěn)定 this.vx *= FRICTION; this.vy *= FRICTION; this.vz *= FRICTION; this.x += this.vx; this.y += this.vy; this.z += this.vz; } getAxis2D() { this.step(); // 3D 坐標(biāo)下的 2D 偏移,暫且只考慮位置,不考慮大小變化 const scale = FOCUS_POSITION / (FOCUS_POSITION + this.z); return { x: this.center.x + (this.x * scale), y: this.center.y - (this.y * scale), }; } }
大功告成!
既然是 3D 的粒子,其實(shí)這上面還有不是文章可做,同學(xué)們可以發(fā)揮想象力來點(diǎn)更酷炫的。
還有什么好玩的
上面是將粒子擺成文字。那咱們當(dāng)然也可以直接寫公式擺出個(gè)造型。
// Actions 中用 func 代替 texts { lifeTime: 100, func: (radius) => { const i = Math.random() * 1200; let x = (i - 1200 / 2) / 300; let y = Math.sqrt(Math.abs(x)) - Math.sqrt(Math.cos(x)) * Math.cos(30 * x); return { x: x * radius / 2, y: y * radius / 2, z: ~~(Math.random() * 30), color: color3 }; } }
再把剛才文字轉(zhuǎn)換形狀的方法用一下
{ lifeTime: Infinity, func: (width, height) => { if(!points.length){ const img = document.getElementById("tulip"); constoffscreenCanvas = document.createElement("canvas"); constoffscreenCanvasCtx = offscreenCanvas.getContext("2d"); const imgWidth = 200; const imgHeight = 200; offscreenCanvas.setAttribute("width", imgWidth); offscreenCanvas.setAttribute("height", imgHeight); offscreenCanvasCtx.drawImage( img, 0, 0, imgWidth, imgHeight); let imgData=offscreenCanvasCtx.getImageData( 0, 0, imgWidth, imgHeight); for ( let i = 0, max = imgData.width * imgData.height; i < max; i++) { if (imgData.data[i * 4 + 3]) { points.push({ x: (i % imgData.width) / imgData.width, y: (i / imgData.width) / imgData.height }); } } } const p= points[~~(Math.random() * points.length)] const radius = Math.min(width * 0.8, height * 0.8); return { x: p.x * radius - radius / 2, y: (1 - p.y) * radius - radius / 2, z: ~~(Math.random() * 30), color: color3 }; } }
完美
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/99899.html
摘要:雙十一光棍節(jié)又要來臨了,每年這個(gè)時(shí)候都是本人最苦悶的時(shí)刻。能不能再給力一點(diǎn)說好的粒子系統(tǒng),現(xiàn)在只是簡(jiǎn)單的畫了一點(diǎn)。 showImg(https://segmentfault.com/img/remote/1460000016908379?w=1100&h=564); ? 雙十一光棍節(jié)又要來臨了,每年這個(gè)時(shí)候都是本人最苦悶的時(shí)刻。日漸消瘦的錢包,愈發(fā)干涸的雙手,雖然變強(qiáng)了,頭卻變涼...
摘要:在兩點(diǎn)間的關(guān)系用夾角和距離很容易表示時(shí),極坐標(biāo)系便顯得尤為有用而在平面直角坐標(biāo)系中,這樣的關(guān)系就只能使用三角函數(shù)來表示。對(duì)于很多類型的曲線,極坐標(biāo)方程是最簡(jiǎn)單的表達(dá)形式,甚至對(duì)于某些曲線來說,只有極坐標(biāo)方程能夠表示。 歡迎大家前往騰訊云+社區(qū),獲取更多騰訊海量技術(shù)實(shí)踐干貨哦~ 本文由郭詩雅發(fā)表于云+社區(qū)專欄 在數(shù)學(xué)中,極坐標(biāo)系(英語:Polar coordinate system)是...
摘要:今天是年月號(hào),一年一度的情人節(jié)又來了,今天對(duì)于很多年輕人來說是個(gè)特別的日子。該程序員在聯(lián)誼會(huì)上認(rèn)識(shí)了某藝術(shù)系女生,同鄉(xiāng)的雙方互有好感。 今天是2018年8月17號(hào),一年一度的情人節(jié)又來了,今天對(duì)于很多年輕人來說是個(gè)特別的日子。但是作為屌絲界的一枚程序員,往往給人的印象是宅,不愛說話,智商不好,也不討好哄女孩子歡心。但是,為什么我身邊的程序員各個(gè)都是高智商,高顏值的小碼農(nóng)呢?或許,他們用...
摘要:源碼很簡(jiǎn)單鍵盤按,然后輸入表白網(wǎng)頁生成器打開網(wǎng)站,填入要表白的內(nèi)容生成好的網(wǎng)頁百度傳情其實(shí)百度很早就有個(gè)傳情功能,直接百度輸入表白即可。 又到520,一個(gè)狂虐單身狗的日子,看看知乎的熱門話題:showImg(https://segmentfault.com/img/remote/1460000019264481); 這里給大家提供幾個(gè)虐狗的新姿勢(shì)。 69 表白從 69 開始,給對(duì)方發(fā)個(gè)...
摘要:完整的資料源碼都打包等你來取哈免費(fèi)滴直接視頻效果展示如下紀(jì)念冊(cè)哪些浪漫至極的表白程序截圖展示如下界面漫天花瓣飛舞。 ?導(dǎo)語 大家好,我是木木子!?? 今日的表白案例上線啦~有沒有期待?安排安排!源碼基地見免費(fèi)源碼哈! 貼心的木子君也給你們好多愛心花瓣、以及表白的小程序!在主頁的左側(cè)哦! 這款...
閱讀 2095·2021-11-02 14:48
閱讀 2771·2019-08-30 14:19
閱讀 2940·2019-08-30 13:19
閱讀 1308·2019-08-29 16:17
閱讀 3245·2019-08-26 14:05
閱讀 2999·2019-08-26 13:58
閱讀 3087·2019-08-23 18:10
閱讀 1113·2019-08-23 18:04