JS圖形編輯器實(shí)現(xiàn)標(biāo)尺功能示例詳解
正文
項(xiàng)目地址:
線上體驗(yàn):
標(biāo)尺指的是畫布上邊和左邊的兩個(gè)有刻度的尺子,作用讓用戶知道他正在編輯的視口所在位置范圍。
我們的需求是:間隔特定的長(zhǎng)度,繪制一個(gè)刻度,并顯示這個(gè)刻度在 X 軸或 Y 軸上的位置。
先看最終實(shí)現(xiàn)效果:
標(biāo)尺功能演示
可以看到,視口移動(dòng)后,標(biāo)尺上的刻度能正確地改變。此外縮放畫布,標(biāo)尺的步長(zhǎng)會(huì)發(fā)生改變,保持一個(gè)比較適合的密度。
實(shí)現(xiàn)思路
總體實(shí)現(xiàn)思路:
- 確定刻度尺的步長(zhǎng)(step)。步長(zhǎng)是和畫布縮放比(zoom)相關(guān)的,zoom 越大,step 就越小;
- 計(jì)算出需要繪制的所有刻度。分別為從視口從左側(cè)到右側(cè),從上邊到下邊的范圍;
- 繪制。繪制上也是有考量的,先繪制背景,然后繪制刻度,最后繪制分界線。
步長(zhǎng)選擇
步長(zhǎng)會(huì)根據(jù) zoom 進(jìn)行設(shè)置,目的是讓視口中的標(biāo)尺能繪制適宜密度的刻度。
假設(shè)我們的步長(zhǎng)固定為 50,不跟隨 zoom 改變,在 100% 看起來效果不錯(cuò):
但當(dāng)你縮小時(shí),會(huì)變成下面這樣:
密度過大,導(dǎo)致數(shù)字重疊。同樣,放大時(shí)則過于稀疏,刻度很難才見到一個(gè),沒能發(fā)揮標(biāo)尺的效用。
步長(zhǎng)怎么計(jì)算呢?
理論上步長(zhǎng)可以是 50,那么 51 好像也行,3 也行。但更建議使用 5 的倍數(shù)、2 的倍數(shù)、25 的倍數(shù)這些作為步長(zhǎng)。
因?yàn)闆]有什么理論參考,所以我還是選擇參考市面上的設(shè)計(jì)工具的步長(zhǎng)變化設(shè)計(jì)。
比如 figma,zoom 落在 [100%, 200%)
的步長(zhǎng)為 50,[200%, 500%)
則是 10 等等。
我的實(shí)現(xiàn)為:
const getStepByZoom = (zoom: number) => { // 可用的步長(zhǎng)列表 const steps = [1, 2, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000]; // 看著 figma 的 step 變化想出的一個(gè)奇怪的規(guī)律 // 然后找出可選步長(zhǎng)列表最近的并大于它的 step 作為最終步長(zhǎng) const step = 50 / zoom; for (let i = 0, len = steps.length; i < len; i++) { if (steps[i] >= step) return steps[i]; } return steps[0]; }; const step = getStepByZoom(zoom);
計(jì)算范圍
這里我講解水平(x 軸)方向的情況。垂直方向同理,就不贅敘了。
首先計(jì)算出視口最左側(cè)和最右側(cè)的 x 坐標(biāo)值。
需要視口坐標(biāo)轉(zhuǎn)場(chǎng)景坐標(biāo)的知識(shí),如果你不懂,看我這篇文章:
《圖形編輯器:場(chǎng)景坐標(biāo)、視口坐標(biāo)以及它們之間的轉(zhuǎn)換》
let startXInScene = viewport.x + startXInViewport / zoom; // 視口坐標(biāo)轉(zhuǎn)場(chǎng)景 let endXInScene = viewport.width + startYInViewport / zoom; // 視口坐標(biāo)轉(zhuǎn)場(chǎng)景
然后找離它們最近的落在刻度上的值。
對(duì)此,我實(shí)現(xiàn)了一個(gè) getClosestVal 方法。
/** * 找出離 value 最近的 segment 的倍數(shù)值 */ const getClosestVal = (value: number, segment: number) => { const n = Math.floor(value / segment); const left = segment * n; const right = segment * (n + 1); return value - left <= right - value ? left : right; }; startXInScene = getClosestVal(startXInScene, step); endXInScene = getClosestVal(endXInScene, step);
得到起點(diǎn)和終點(diǎn),我們可以開始循環(huán)了,從 startXInScene 開始,每次循環(huán)加一個(gè) step,直至達(dá)到末尾為止。
ctx.textAlign = 'center'; // 文字水平居中對(duì)齊 while (startXInScene <= endXInScene) { ctx.strokeStyle = setting.rulerMarkStroke; ctx.fillStyle = setting.rulerMarkStroke; // 場(chǎng)景轉(zhuǎn)回視口再繪制??潭染€不能直接在場(chǎng)景中繪制,因?yàn)榭s放變換會(huì)導(dǎo)致線的粗細(xì)變化 const x = (startXInScene - viewport.x) * zoom; // 繪制刻度 ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x, y + setting.rulerMarkSize); ctx.stroke(); ctx.closePath(); // 刻度值則用場(chǎng)景坐標(biāo)的值 ctx.fillText(String(startXInScene), x, y - 4); // +step,指針移動(dòng) startXInScene += step; }
垂直方向的標(biāo)尺同理,只是稍微特殊的是刻度值文字需要多做一個(gè) -90 度的旋轉(zhuǎn)。
export const rotateInCanvas = ( ctx: CanvasRenderingContext2D, angle: number, cx: number, cy: number ) => { ctx.translate(cx, cy); ctx.rotate(angle); ctx.translate(-cx, -cy); }; rotateInCanvas(ctx, -HALF_PI, x, y);
繪制順序
繪制順序需要注意一下,先后順序?yàn)椋?/p>
- 繪制兩個(gè)標(biāo)尺的背景色;
- 繪制刻度值;
- 用一個(gè)和背景色同色的矩形蓋掉左上角那個(gè)方形,那個(gè)地方不能有刻度值,不如兩個(gè)標(biāo)尺的刻度會(huì)重疊。你也可以在繪制刻度值時(shí),用裁切(ctx.clip)不讓繪制到那個(gè)方形區(qū)域上;
- 繪制兩條分割線;
以上就是JS圖形編輯器實(shí)現(xiàn)標(biāo)尺功能示例詳解的詳細(xì)內(nèi)容,更多關(guān)于JS圖形編輯器實(shí)現(xiàn)標(biāo)尺的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JS實(shí)現(xiàn)可恢復(fù)的文件上傳示例詳解
這篇文章主要為大家介紹了JS實(shí)現(xiàn)可恢復(fù)的文件上傳示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12JS實(shí)現(xiàn)大數(shù)相加大數(shù)相乘示例詳解
這篇文章主要為大家介紹了JS實(shí)現(xiàn)大數(shù)相加大數(shù)相乘示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08Javascript基礎(chǔ)知識(shí)中關(guān)于內(nèi)置對(duì)象的知識(shí)
這篇文章主要介紹了Javascript基礎(chǔ)知識(shí)中關(guān)于內(nèi)置對(duì)象的相關(guān)知識(shí)的相關(guān)資料,需要的朋友可以參考下面小編薇大家?guī)淼木饰恼?/div> 2021-09-09Css-In-Js實(shí)現(xiàn)classNames庫源碼解讀
這篇文章主要為大家介紹了Css-In-Js實(shí)現(xiàn)classNames庫源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12LoadRunner調(diào)用JS加密后登錄實(shí)現(xiàn)
這篇文章主要為大家介紹了LoadRunner調(diào)用JS加密后登錄實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06微信小程序 滾動(dòng)到某個(gè)位置添加class效果實(shí)現(xiàn)代碼
這篇文章主要介紹了微信小程序 滾動(dòng)到某個(gè)位置添加class效果實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-04-04最新評(píng)論