前幾天有很多人問到了如何繪製高德地圖那樣的導航箭頭線效果,當時想了想並不難就先用canvas 做了,然後整合到mapbox 上,遷移到其他map lib 也只需要應用相應的地理轉螢幕座標函式。
線的繪製樣式
在canvas 的應用中我們經常會遇到各種線樣式的繪製,比如虛線,漸變線,帶pattern 線(箭頭,鐵軌圖示等),或者虛線與pattern 的動畫效果。如下圖所示,總結下實現方法。
虛線樣式
- 利用 CanvasRenderingContext2D.setLineDash 的方法設定虛線樣式, 接受一個陣列型別的引數([solid: number, empty:number]) ,表示實線虛線的畫素比例.
比如 ctx.setLineDash([10, 5]),就可以畫出上圖的虛線效果。發揮想象可以做出更多奇特效果。比如讓虛線動起來,有走馬燈的感覺。。
漸變線樣式
通過 createLinearGradient 函式建立漸變,然後設定其漸變色段, 賦值給strokeStyle,漸變效果如開頭圖所示
// 建立一個從左到右的漸變,從綠色漸變到幾乎透明,
var gradient = ctx.createLinearGradient(0, 0, 600, 0);
gradient.addColorStop(0, "rgba(0,255,100,0.9)");
gradient.addColorStop(1, "rgba(255,255,255,0.1)");
ctx.strokeStyle = gradient;
複製程式碼
有個問題就是如果需要漸變方向符合線條走向,這是常見需求,只需要提前算下每條線的範圍和方向,建立對應的LinearGradient 即可,其實類似於下面的箭頭繪製,需要反算 atan 角度一樣的。
icon pattern 的線樣式
最後這個算是綜合的應用,我寫了些canvas functions 放到了之前的canvasOverlay 裡面去用,可以方便的整合到各種支援canvas 的lib 裡面用。
基本思路就是:(懶得畫圖了,思路比較簡單)
-
每一個有向線段根據startPnt, endPnt 座標反算 atan 弧度角
-
設定一個stepSize,線上段上生成繪製圖示的座標,把ctx.原點平移到這個點,並且旋轉弧度角去繪製 圖示的偏向(熟悉canvas 的應該都明白,通過操作canvas 座標軸去繪製旋轉要素)
需要注意的是,atan弧度角的計算在第二三象限,會跟第一四象限混淆。比如向左下角的有向線的向量是兩個負值,但是tan 值是正的,跟第一象限一樣,所以反算的時候也會算出來小於90度的角,實際上是大於180 的角度了,需要 + Math.PI
大概的繪製過程,code as follow:
function generatePoints(startP, endP, stepSize = 30, ctx, aniOffset = 0.5, img) {
let radA = Math.atan((endP[1] - startP[1]) / (endP[0] - startP[0]));
if ((endP[0] - startP[0]) < 0) {
radA += Math.PI;
}
const dist = calcDist(startP, endP);
let points = [];
const steps = dist / stepSize;
const drawImg = (pX, pY) => {
if (img && ctx) {
ctx.save();
ctx.translate(pX , pY); // consider img position and imgWidth/Height.
ctx.rotate(radA);
ctx.drawImage(img, -img.width / 2, -img.width/2);
ctx.restore();
}
}
// gen points by stepSize.. if enable corner arrow, start s with (0~1) float number.
for (let s = aniOffset; s <= steps; s += 1) {
const pX = Math.round(startP[0] + s * stepSize * Math.cos(radA));
const pY = Math.round(startP[1] + s * stepSize * Math.sin(radA));
points.push([pX, pY]);
drawImg(pX, pY);
}
// console.warn(`icon Number: ${points.length}`);
return points;
}
複製程式碼
就簡單寫到這裡,整合mapbox 的DEMO
最近還有個問題提到比較多,關於canvas 上的圖示如何貼合地圖的傾斜,這也是個視覺上的問題。大抵上可以通過CSS3d 或者 canvas 的透視去做,前者應該更簡單些。有空再實踐下
因為之前也寫過一些canvas 的文章,比如繪製風向圖,希望形成一個系列,所以這次就叫第二篇,下一篇應該是icon與傾斜角的貼合實踐