畫影圖形指描畫犯人面影,懸賞通緝。這裡用法明顯有問題,不過取其表義而已。
在一個前端看來,畫圖有三種方法,Cavas,SVG 以及 CSS。至於三者優劣,將在此針對各種圖形做逐一比較,有方,圓,橢圓,扇形,多邊形,漸變,文字處理以及動畫
以下是在 codepen 的程式碼以及實際效果地址,可開啟實際效果地址進行除錯
Stroke and Fill
描邊與填充是圖形的基本屬性。
CSS
在 CSS 中是 border-color
以及 background-color
屬性。
.rect {
border: 1px solid #fff;
background-color: #000;
width: 100px;
height: 100px;
}
複製程式碼
SVG
在 SVG 中是 fill
和 stroke
。可以直接作為 element
的屬性,另外也可以寫到 css 樣式中
<rect width="100" height="100" fill="#fff" stroke="#000"></rect>
複製程式碼
也可以作為 css 樣式
.rect {
fill: #fff;
stoke: #000;
}
複製程式碼
除了基本的描邊,SVG 也有以下屬性,此是 canvas 以及 css 所不及的
- stroke-dasharray
- stroke-dashoffset
- stroke-linecap
- stroke-linejoin
Canvas
在 Canvas 中,是 fillStyle
及 strokeStyle
屬性。
const canvas = document.getElementById('rect')
const ctx = canvas.getContext('2d')
ctx.fillStyle = '#000'
ctx.strokeStyle = '#fff'
ctx.fillRect(0, 0, 100, 100)
ctx.fillRect(115, 0, 100, 100)
複製程式碼
矩形
CSS
不作介紹
.rect {
width: 100px;
height: 100px;
border-radius: 15px;
}
複製程式碼
SVG
<svg>
<rect width="100" height="100" rx="15" ry="15"></rect>
</svg>
複製程式碼
由 rx
及 ry
設定圓角矩形大小
Canvas
const canvas = document.getElementById('rect')
const ctx = canvas.getContext('2d')
ctx.fillRect(0, 0, 100, 100)
ctx.fillRect(115, 0, 100, 100)
複製程式碼
在效果地址中可以看到沒有使用 Canvas 繪出的圓角矩形, Canvas 沒法直接通過矩形的 API 直接繪製出圓角矩形,這是它的硬傷
以下是通過其它方法繪製出來的圓角矩形,略(非常)麻煩
CSS3 border radius to HTML5 Canvas
多邊形
多邊形由多個點連線而成,只需要確定多個點的位置,便能確定多邊形。在效果頁面中,使用五角星作為示例
構成五角星的五個點分別是 [[81, 95], [0, 36], [100, 36], [19, 95], [50, 0]]
CSS
SVG
svg 使用元素 polyline
以及 polygon
。polygon
會把終點和起點連線起來,形成閉合圖形。
points
屬性指連成多邊形的點。
fill-rule
決定哪裡是圖形的內部。nonzero
代表如果被路徑所包圍,即是內部,fill-rule
代表從某一點出發,到無限遠處,如果只途經奇數條邊,則在圖形內部。因為效果地址中的五角星是空心的。
<polyline points="81, 95 0, 36 100, 36 19, 95 50, 0" fill-rule="evenodd"></polyline>
複製程式碼
Canvas
canvas 需要使用 path
來繪製路徑。
使用 moveTo
及 lineTo
來繪製直線,closePath
代表把終點和起點連線起來,相當於 svg 的 polygon
。
canvas 的 fill
方法也有 fillRule
屬性。
const canvas = document.getElementById('star')
const ctx = canvas.getContext('2d')
const drawStar = ({ stroke = false, fillRule = 'nonzero' } = {}) => {
ctx.beginPath()
ctx.moveTo(81, 95)
ctx.lineTo(0, 36)
ctx.lineTo(100, 36)
ctx.lineTo(19, 95)
ctx.lineTo(50, 0)
ctx.closePath()
// fill 的時候自動 closePath
stroke ? ctx.stroke() : ctx.fill(fillRule)
}
drawStar({ stroke: true })
ctx.translate(115, 0)
drawStar()
ctx.translate(115, 0)
drawStar()
ctx.translate(115, 0)
drawStar({ fillRule: 'evenodd' })
複製程式碼
圓,扇形和橢圓
圓的標準方程為 (x-a)²+(y-b)²=r²
,其中 (a, b) 為圓心,r 為半徑。只要確定了圓心和半徑便能確定一個圓。
扇形為圓周的一部分以及對應的圓周角組成,是圓的一部分。
CSS
SVG
svg 使用元素 circle
代表圓,(cx, cy)
為圓心,r
為半徑。使用元素 ellipse
代表橢圓,rx
和 ry
代表長軸和短軸。
<circle cx="50" cy="50" r="49"></circle>
<ellipse cx="50" cy="50" rx="30" ry="40"></ellipse>
複製程式碼
svg 對於扇形沒有現成的元素,需要使用 path
來作扇形。當然 canvas 除了矩形剩餘圖形都是使用 path
來繪製出來。
path
中的 d
代表路徑,d
有諸多屬性
- M,Move,與 canvas 的 move 方法一致
- L,Line,與 canvas 的 line 方法一致
- A,Arc,七個引數
(x, y, r, d1, d2, direction)
,A 用來畫橢圓,詳細參考 MDN SVG Paths。
如果使用 svg 畫扇形的話,需要確認圓弧的兩個端點以及圓心的位置,遠沒有 canvas 直接使用圓心角確定一個圓方便地多
<path d="M 50 50 L 99 50 A 49 49 0 1 0 50 99"></path>
複製程式碼
Canvas
canvas 使用方法 arc
進行圓的繪製,有六個引數 void ctx.arc(x, y, radius, startAngle, endAngle [, anticlockwise]);
。使用 startAngle
和 endAngle
可以很方便地繪製扇形。 但是沒法繪製橢圓是硬傷
const canvas = document.getElementById('circle')
const ctx = canvas.getContext('2d')
function drawArc (radius, { anticlockwise = false, stroke = false } = {}) {
ctx.beginPath()
ctx.arc(50, 50, 49, 0, radius / 180 * Math.PI * 2, anticlockwise)
ctx.lineTo(50, 50)
ctx.closePath()
stroke ? ctx.stroke() : ctx.fill()
}
drawArc(360)
ctx.translate(115, 0)
drawArc(60, { anticlockwise: true })
ctx.translate(115, 0)
drawArc(60)
ctx.translate(115, 0)
ctx.scale(0.6, 0.8)
ctx.translate(20, 10)
drawArc(360)
複製程式碼
漸變
漸變分為線性漸變和徑向漸變
CSS
略
SVG
linearGradient
代表線性漸變,radialGradient
代表徑向漸變。
<svg>
<defs>
<linearGradient id="linear" x1="0" y1="0" x2="0.3" y2="0.3" spreadMethod="reflect">
<stop offset="0" stop-color="red"></stop>
<stop offset="100%" stop-color="#fff"></stop>
</linearGradient>
<radialGradient id="radial" cx="0.5" cy="0.5" r="0.5" fx="0.8" fy="0.8">
<stop offset="0" stop-color="red"></stop>
<stop offset="100%" stop-color="#fff"></stop>
</radialGradient>
</defs>
<rect x="0" y="0" width="100" height="100" fill="url(#linear)"></rect>
<circle cx="50" cy="50" r="50" fill="url(#radial)" transform="translate(115, 0)"></circle>
</svg>
複製程式碼
Canvas
createLinearGradient
代表線性漸變,createRadialGradient
代表徑向漸變。
const canvas = document.getElementById('grad')
const ctx = canvas.getContext('2d')
// canvas 實現 reflect 和 repeat 需要自己程式設計實現
const grad1 = ctx.createLinearGradient(0, 0, 100, 100)
grad1.addColorStop(0, 'red')
grad1.addColorStop(0.3, '#fff')
grad1.addColorStop(0.6, 'red')
grad1.addColorStop(0.9, '#fff')
ctx.fillStyle = grad1
ctx.fillRect(0, 0, 100, 100)
const grad2 = ctx.createRadialGradient(50, 50, 50, 80, 80, 0)
// 注意與 svg 中 stop 的顏色值相反
grad2.addColorStop(0, '#fff')
grad2.addColorStop(1, 'red')
ctx.fillStyle = grad2
ctx.translate(115, 0)
ctx.beginPath()
ctx.arc(50, 50, 50, 0, Math.PI * 2)
ctx.fill()
複製程式碼
文字
效果頁面上的示例是垂直居中
CSS
略
SVG
svg 使用元素 text
代表文字,屬性 text-anchor
和 alignment-baseline
控制垂直居中。
<text x="50" y="50" text-anchor="middle" alignment-baseline="middle">垂直居中</text>
複製程式碼
Canvas
canvas 使用屬性 textAlign
和 textBaseline
控制垂直居中。
const canvas = document.getElementById('text')
const ctx = canvas.getContext('2d')
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.fillText('垂直居中', 50, 50)
複製程式碼
動畫
效果頁面的示例是一個簡單的 loading 動畫
CSS
說到動畫和 css, animation
以及 @keyframes
最常被用來製作動畫。
但是最新出了 css 的屬性 offset-path
可以像 svg 的 animateMotion
一樣沿著特定軌跡運動。具體可參考以下
SVG
關於動畫的元素有三種
- animate
- animateTransform
- animateMotion,沿著特定軌跡進行運動
關於以下 loading 動畫繪製的原理是,圓的半徑從大變小,顏色由有至透明
<svg>
<circle cx="50" cy="50" r="49">
<animate attributeName="r" values="50; 5; 50" keyTimes="0; 0.5; 1" dur="3s" repeatCount="indefinite">
</animate>
<animate attributeName="fill" values="red; white; red" dur="3s" repeatCount="indefinite">
</animate>
<circle/>
</svg>
複製程式碼
Canvas
canvas 的動畫就比較簡單粗暴了。大致步驟便是 繪製幀,清畫板,繪製幀,迴圈往復。優點是可定製話程度高。