3D球體詞雲

效果以下:
html

此次作的是組件重構,以前這個組件我是在網上找了個插件,基於tagCanvas插件開發的,可是這個組件是15年左右,很久遠了哇,太老了不太好用,知足不了目前新項目的需求。
前端

吳大哥分享了個網上看到的demovue

https://github.com/kongkong99/sample-reels/blob/master/src/views/3D-wordCloud/index.vuegit

這個demo是用vue寫的,我須要改動下適用於咱們開發框架的組件。github

目前工做的開發組,組件項目是基於LitElement,其實它就是一套web components,要使用到shadowDomweb

這裏我主要是記錄一下,代碼轉換的過程和組件代碼須要本身注意的幾個地方。typescript

根據demo代碼,首先是在render函數中,定義個容器,該容器的寬高是組件的寬高。在這個容器中須要根據數據量生成span標籤,顯示文本內容。數組

通常動態生成html結構,是封裝成一個方法,方法調用用${}包起來。微信

<style> .container { position: relative; width: ${this.width}px; height: ${this.height}px; } .tag { display: block; position: absolute; left: 0px; top: 0px; font-size: ${this.fontSize * rx}px; }</style><div class="container">${this.createTags()}</div>

demo是用v-for循環數據生成span標籤,組件的寫法須要改動成以下這樣:框架

public createTags() { const { color, contentEle } = this; return this.wordData.map((item: IWordData, index: number) => { const styleObj = contentEle[index] ? { ...contentEle[index].style } : {}; return html` <span class="tag" style=" color: ${color[index % color.length]}; opacity: ${styleObj.opacity}; z-index: ${styleObj.zIndex}; transform: ${styleObj.transform}; "          >${item.name}</span> `; });}

這裏遇到過一個問題,contentEle這個數組,沒有contentEle[index]是否存在的話會報undefined的錯,拿不到style

demo代碼能夠知道這個style是存在每一個tag運行是的位置數據,也就是絕對定位的topleft值,以及translate的值,還有一些層級透明度的值。demovue...contentEle[index].style這個寫法,組件裏不適用。

這個擴展運算符是對對象的深拷貝,須要用{}括起來。組件的style的寫法,只能將對象中值挨個拿出來寫,不能像vue中直接寫個對象。

demovue寫法中created周期函數中初始化了datacontentEle數組,這個階段作的事情等同於組件寫法的isFirstUpdated中執行階段,可是contentEle數組是基於數據生成的,可是isFirstUpdated中還拿不到數據,所以在isFirstUpdatedelse中並監聽數據的變化時執行相同的代碼。

public updateComponent( isFirstUpdated: boolean, properties: Map<string | number | symbol, unknown>) { if (!isFirstUpdated) { if (properties.has("wordData")) { this.contentEle = this.wordData.map(() => ({ x: 0, y: 0, z: 0, style: {}, })); this.initSpin();    }  }}

下面的代碼和demo的一致。

其中執行動畫的代碼作了一些改變。須要實現的效果是,球體詞雲自動旋轉,鼠標移入時中止,移除回覆旋轉。

isFirstUpdated中執行以下代碼:

window.addEventListener("mousemove", (e: any) => { const container = this.shadowRoot!.querySelector(".container"); if (e.path.includes(container)) { this.pauseAnimate(); } else { this.restartAnimate(); }});

先獲取該組件shadowDom中定義的container容器,在鼠標移動的時候,來判斷鼠標事件e中的pathdom節點數上有沒有當前這個組件的容器,有的話就說明是鼠標移入了就須要中止旋轉,不然就是移出能夠恢復旋轉。

e.path會拿到當前鼠標所在的dom以及這個dom節點往上一直到window的全部節點。

暫停和恢復動畫的方法中只是修改一個全局的布爾值類型的變量。

再根據這個變量值來判斷是否調用demoanimate方法裏調用的那幾個方法。

public spinAnimate() { if (!this.animatePause) { this.rotateX(); this.rotateY(); this.move(); } this.animateId = requestAnimationFrame(() => { cancelAnimationFrame(this.animateId); this.spinAnimate(); });}

demoanimate方法名和組件引用的基類中的animate方法重名,因此改了個方法名。

代碼中關於球體的構建用到了三角函數,我實在沒看懂。可是代碼中不少地方都是寫的固定值來計算位置,可組件是須要拖動邊框改變寬高大小的,測試發現組件寬高很大時候就出現bug了,最好的解決方案就是定義個初始狀態下寬高的值,計算一下組件寬高和初始寬高的比例,再將代碼中全部的固定值都乘上這個比例值。

const rx = this.width / this.originWidth;const ry = this.height / this.originHeight;

最終組件效果:

最後,默默感謝吳大哥,但願本身儘早能達到大佬們的技術水平。

本文分享自微信公衆號 - 前端一塊兒學(gh_3ba18d51f982)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索