我們知道,在一些內(nèi)容創(chuàng)作網(wǎng)站,直接貼二維碼的話文章會(huì)被限流警告,那么如何才能夠光明正大的貼出二維碼呢?看完本文,相信你就會(huì)有答案了。最終我們想要實(shí)現(xiàn)這樣的一個(gè)效果:
最終效果
本文最終的效果可以訪問(wèn)https://gallery.xieyufei.com/about查看
要實(shí)現(xiàn)這樣的效果,首先我們需要將二維碼進(jìn)行建模處理;網(wǎng)絡(luò)上也有很多建模工具,這里筆者推薦使用開(kāi)源且好用的Blender4進(jìn)行建模;整個(gè)建模的過(guò)程較為繁瑣,因此需要一定的耐心和細(xì)心;如果有更好的建模方式,歡迎在評(píng)論區(qū)留言討論。
選中右上角的Z軸,將圖片拖拽入編輯器中,最好將二維碼的四周白邊進(jìn)行裁切;然后調(diào)整其XYZ軸位置,讓其居中:
導(dǎo)入
Shift+A鍵,我們添加一個(gè)網(wǎng)格=>平面進(jìn)來(lái),再次調(diào)整位置,然后選擇右上角的切換透視模式,快捷鍵是Alt+Z鍵;開(kāi)啟透視模式后,我們就可以調(diào)整平面的大小,和二維碼中間部分大小相同即可。
添加并調(diào)整平面
選擇平面后,繼續(xù)點(diǎn)擊Tab鍵進(jìn)入編輯模式,Ctrl+R添加分割線,在平面的橫向和縱向都添加多個(gè)分割線,滾動(dòng)鼠標(biāo)滾輪可以增加或減少數(shù)量,當(dāng)數(shù)量剛好可以切割每個(gè)二維碼的方塊時(shí)點(diǎn)擊右鍵確定:
添加分割線
點(diǎn)擊左上角的面選擇模式以及擴(kuò)展當(dāng)前選中項(xiàng)模式,將白色塊的部分選中出來(lái),然后點(diǎn)擊del鍵進(jìn)行刪除,選擇面選項(xiàng),這個(gè)過(guò)程需要耐心慢慢選擇:
注意框選面中間的小點(diǎn),才能選中面。
選擇刪除面
刪除后我們得到了這樣一個(gè)圖形,我們發(fā)現(xiàn)依然可以進(jìn)行掃碼:
刪除完成
我們給每個(gè)黑色的小方塊同時(shí)添加橫向和縱向的分割線,選擇所有的面,點(diǎn)擊del刪除,選擇僅邊和面選項(xiàng)
這一步也需要細(xì)心和耐心,當(dāng)然你也可以在前面第一次分割的時(shí)候,分割得更細(xì)。
刪除面和邊
面和邊刪除了,我們的界面上看似什么都沒(méi)有了,不過(guò)別著急,然后右上角選擇點(diǎn)模式,我們就能看到刪除了邊和面之后,還有很多的小點(diǎn)剩余下來(lái),我們只需要保留黑色方塊中心的點(diǎn);這里我們?cè)邳c(diǎn)模式下框選黑色方塊中的點(diǎn)后,然后使用Ctrl+I鍵進(jìn)行反向選擇,刪除其他所有的點(diǎn):
選擇點(diǎn)
最終我們得到這樣一個(gè)點(diǎn)狀的圖形,將其導(dǎo)出到glb文件即可:
最終生成的
我們的模型處理完成后,可以導(dǎo)入到three.js中來(lái)了;我們導(dǎo)入模型后,由于模型的大小和位置可能不是我們需要的,可以對(duì)geometry進(jìn)行縮放、旋轉(zhuǎn)、平移操作,調(diào)整到頁(yè)面上合適的位置即可:
const loader = new GLTFLoader();let qrGeometryloader.load("/models/qr.glb", (gltf) => { const geometry = gltf.scene.children[0].geometry; geometry .scale(20, 20, 20) .rotateX((90 / 180) * Math.PI) .rotateY((90 / 180) * Math.PI) .translate(0, 0, -20); qrGeometry = geometry;});然后就該用到我們的粒子Points了,我們先用隨機(jī)數(shù)生成一堆的粒子,然后將Camera鏡頭放到粒子堆的邊緣進(jìn)行移動(dòng),就可以產(chǎn)生粒子在旋轉(zhuǎn)的效果:
const getRandomPos(index) { const x = Math.random() * 90 - 45; const y = Math.random() * 90 - 45; const z = Math.random() * 90 - 45; return [x, y, z];}const initPoints = () => { const randomGeometry = new BufferGeometry(); const verticles = []; for (let i = 0; i < 8000; i++) { const [x, y, z] = getRandomPos(i) verticles.push(x, y, z); } randomGeometry.setAttribute( "position", new Float32BufferAttribute(verticles, 3) ); const material = new PointsMaterial({ color: 0x333333, size: 0.8, map: new TextureLoader().load("gradient.png"), }); const pt = new Points(randomGeometry, material); screen.add(pt); this.pt = pt;};但是這樣生成出來(lái)的隨機(jī)點(diǎn)呈現(xiàn)出來(lái)是一個(gè)立方體,所以攝像機(jī)在移動(dòng)時(shí)需要掌控好位置,如果距離原點(diǎn)太遠(yuǎn),就會(huì)出現(xiàn)粒子稀疏不同的情況;那么我們優(yōu)化隨機(jī)函數(shù),讓隨機(jī)點(diǎn)生成在一個(gè)橢圓體的范圍內(nèi),這樣相機(jī)在移動(dòng)時(shí)粒子就比較均勻了,不會(huì)出現(xiàn)分布不均的情況;這里引入三維空間下的橢圓計(jì)算公式:
橢圓的計(jì)算方程
這里的計(jì)算邏輯也很簡(jiǎn)單,有三個(gè)變量,我們通過(guò)控制變量的方式,先生成Y軸和Z軸的隨機(jī)值,然后套用計(jì)算公式,就可以計(jì)算得到X軸的最大值max,再根據(jù)這個(gè)max值隨機(jī)生成X軸的的坐標(biāo)即可:
const getRandomPos = (index) => { const MAX_Y = 60; const MAX_Z = 80; const MAX_X = 80; const y = Math.random() * MAX_Y * 2 - MAX_Y; const z = Math.random() * MAX_Z * 2 - MAX_Z; const max = Math.sqrt( (1 - Math.pow(y, 2) / MAX_Y / MAX_Y - Math.pow(z, 2) / MAX_Z / MAX_Z) * MAX_X * MAX_X ); const x = Math.random() * max * 2 - max; return [x, y, z];};我們看到Y(jié)軸的數(shù)值比X和Z軸都小一點(diǎn),因此整個(gè)橢圓體會(huì)偏扁一點(diǎn),生成出來(lái)的圖形也符合我們的預(yù)期:
橢圓體
這樣,我們的相機(jī)在旋轉(zhuǎn)時(shí)粒子分布就相對(duì)比較均勻了;我們將鏡頭拉到粒子的邊緣,然后繞著邊緣做緩慢的環(huán)繞運(yùn)動(dòng)就可以看到粒子的旋轉(zhuǎn)效果了:
const changeCameraView = () => { new Tween.Tween({ x: 0, }) .to( { x: 80, }, 2400 ) .onUpdate((pos) => { const { x } = pos; const z = Math.sqrt(80 * 80 - Math.pow(x, 2)); this.camera.position.x = x; this.camera.position.z = z; this.camera.lookAt(new Vector3(0, 0, 0)); this.camera.updateProjectionMatrix(); }) .start();};隨機(jī)粒子生成后,我們就可以將randomGeometry模型轉(zhuǎn)換成我們上面的qrGeometry模型了,引入我們的切換模型函數(shù),這里的函數(shù)在粒子云效果的實(shí)現(xiàn)里面已經(jīng)詳細(xì)解釋了,這里不再贅述了:
const changeGeometry = (toArray, duration = 1500) => { const nowFloatArray = this.pt.geometry.attributes.position.array; const tos = this.mixFloatArray(nowFloatArray, toArray); new Tween.Tween({ ...Array.from(nowFloatArray), }) .to(tos, duration) .easing(Tween.Easing.Quadratic.InOut) .onComplete(() => { this.isChanging = false; }) .onUpdate((pos) => { for (let key in pos) { const val = pos[key]; const idx = Number(key); this.pt.geometry.attributes.position.array[idx] = val; } this.pt.geometry.attributes.position.needsUpdate = true; }) .start();};在旋轉(zhuǎn)鏡頭的同時(shí),我們進(jìn)行模型的切換:
this.changeCameraView();setTimeout(() => { this.changeGeometry(qrGeometry.attributes.position.array, 1600);}, 1600);本文最終的效果可以訪問(wèn)https://gallery.xieyufei.com/about查看
本文根據(jù)前文粒子云的實(shí)現(xiàn)效果,擴(kuò)展了具體如何來(lái)實(shí)現(xiàn)一個(gè)二維碼的粒子化效果;通過(guò)建模工具Blender,可以將我們的二維碼建立模型后導(dǎo)入three.js中;適合網(wǎng)頁(yè)上需要呈現(xiàn)展示二維碼效果的地方。
本文鏈接:http://m.www897cc.com/showinfo-26-92164-0.html二維碼還能這么玩?制作一個(gè)3D動(dòng)態(tài)粒子二維碼!
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com
上一篇: 45 個(gè)每個(gè)開(kāi)發(fā)人員都應(yīng)該知道的 JavaScript 超級(jí)技巧
下一篇: Python裝飾器泛化公有和私有屬性