本章主要介紹以下幾個方面:
- 陰影效果shadowColor, shadowOffsetX...
- 全局透明度globalAlpha設(shè)置麻顶,圖像疊加時的效果globalCompositeOperation
- clip() 設(shè)置繪制區(qū)域芦拿,探照燈效果和基本canvas的動畫模型
- 零和原則制作剪紙效果
- isPointInPath()判斷點的位置器净,clearRect()清空矩形畫布
一.陰影效果
這個效果和css中的 'box-shadow' 類似揭糕。canvas中它有幾個屬性:
- shadowColor: 陰影的顏色
- shadowOffsetX: x軸的偏移
- shadowOffsetY: Y軸的偏移
- shadowBlur: 設(shè)置模糊值
示例:
ctx.shadowColor = 'rgba(0,0,0,0.5)'
ctx.shadowOffsetX = -2
ctx.shadowOffsetY = -1
ctx.shadowBlur = 2
ctx.font = 'bolder 50px Ubuntu'
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.fillStyle = 'orangered'
ctx.fillText('James Sawyer', 200, 200, 400) // 400為最大寬度
二.globalAlpha && globalCompositeOperation
globalAlpha
這個屬性相對來說比較簡單赴背,主要是設(shè)置一個全局的alpha值派殷。這個值和 rgba(0,0,0,alpha) 中的alpha的效果一致离赫,只不過這個是設(shè)置全局的透明度
ctx.globalAlpha = 0.7
globalCompositeOperation
這個值的屬性比較多,主要用于設(shè)置圖形相互疊加時顯示的效果呈础,其顯示效果可以分為3組
1.后繪制的圖形B在上面舆驶,前面繪制的圖形A被遮蓋
- source-over: 默認(rèn)值,后面的圖形B在A的上面
- source-atop: 后面的圖形B只顯示與前面圖形A的交叉部分而钞,A則全部顯示 (A + A∩B)
- source-out: 只顯示B圖形未與圖形A未交叉部分沙廉,A不顯示 (A∪B - A - A∩B)
- source-in: 只顯示圖形交叉部分(A∩B)
2.后繪制的圖形B在前面繪制的圖形A的下面
這種情況和上面的情況就是圖形的z-index改變了,其余的一致臼节。也有3種屬性
- destination-over: A在B上面
- destination-atop: 后面的圖形A只顯示與前面圖形B的交叉部分撬陵,B則全部顯示 (B + A∩B)
- destination-out: 只顯示A圖形未與圖形B未交叉部分,B不顯示 (A∪B - B - A∩B)
- destination-in: 只顯示圖形交叉部分(A∩B)
3.其他情形
- lighter: 交叉部分的顏色變淺
- copy: 只復(fù)制最后繪制的圖形B
- xor: 交叉部分被去掉即(A∪B - A∩B)
除了上面的11種网缝,還有更多的選項:
三.clip() 將畫布設(shè)置成當(dāng)前的
創(chuàng)建剪輯區(qū)域
表示使用設(shè)置的路徑區(qū)域作為繪制的范圍環(huán)境
例如:
ctx.beginPath()
ctx.arc(400, 400, 150, 0, Math.PI * 2)
ctx.stroke() // 用于做輔助線巨税,可以不加這行
# clip()表示使用上面的圓圍成的區(qū)域作為繪制環(huán)境
ctx.clip()
ctx.font = 'bold 150px Ubuntu'
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.fillStyle = 'black'
ctx.fillText('CANVAS', canvas.width/2, canvas.height/2)
則其效果為:
探照燈效果和canvas基本動畫
探照燈利用clip() 函數(shù)將可視區(qū)域隨動畫的改變而改變
動畫效果
動畫效果利用 setInveral()函數(shù)來不停的更新畫布,到達(dá)動畫的效果
window.onload = function() {
var canvas = document.querySelector('#canvas');
canvas.width = 800;
canvas.height = 800;
# 設(shè)置探照燈對象模型
/*
* @param (x, y): 表示圓心坐標(biāo)
* @param radius: 圓的半徑
* @param vx, vy: 水平和垂直方向的速度,通過他們控制速度大小
*/
var searchLight = {
x: 400,
y: 400,
radius: 150,
vx: Math.random() * 5 + 10,
vy: Math.random() * 5 + 15
};
# 通過setInterval來更新模型的位置
# 每40ms更新一次
setInterval(() => {
draw(ctx);
update(canvas.width, canvas.height);
}, 40);
}
function draw(ctx) {
# 繪制之前先清空畫布
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
# 講畫圖繪制為黑色
ctx.beginPath();
ctx.fillStyle = 'black';
ctx.fill()
# 繪制圓形區(qū)域
ctx.save()
ctx.beginPath()
ctx.arc(
searchLight.x, searchLight.y,
searchLight.r,
0, Math.PI * 2
);
ctx.fill();
# 將上面的區(qū)域作為剪輯區(qū)域
ctx.clip();
ctx.font = 'bold 150px Ubuntu';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = '#058';
ctx.fillText('CANVAS', canvas.width/2, canvas.height/2)
ctx.restore();
}
# 小球運動模型粉臊,很基本的邏輯判斷
function update(canvasWidth, canvasHeight) {
searchLight.x += searchLight.vx;
searchLight.y += searchLight.vy;
# 如果小球超出了左邊的邊界草添,則速度反向,x點變?yōu)閳A的半徑
if (searchLight.x - searchLight.radius <= 0) {
searchLight.vx = -searchLight.vx;
searchLight.x = searchLight.radius;
}
# 如果小球超出了右邊的邊界扼仲,
# 則速度反向远寸,x點變?yōu)?畫布寬度 - 圓的半徑
if (searchLight.x + searchLight.radius >= canvasWidth) {
searchLight.vx = -searchLight.vx;
searchLight.x = canvasWidth - searchLight.radius;
}
# y軸方向 基本同上
if (searchLight.y - searchLight.radius <= 0) {
searchLight.vy = -searchLight.vy;
searchLight.y = searchLight.radius;
}
if (searchLight.y + searchLight.radius >= canvasHeight) {
searchLight.vy = -searchLight.vy;
searchLight.y = canvasHeight - searchLight.radius;
}
}
四.路徑方向和零和原則
零和原則: 封閉圖形內(nèi)部是否填充顏色和路徑的方向有關(guān)抄淑,從封閉圖形的內(nèi)部引出一條射線,指定一個方向為正方向驰后,與正方向相交則+1肆资,與反方向相交-1,最后總和不為0灶芝,則該區(qū)域為填充區(qū)域郑原;總和為0則該區(qū)域不進(jìn)行填充
如圖,淺藍(lán)色部分相交之和為0夜涕,所以不進(jìn)行填充
可以根據(jù)這個原則來繪制出剪紙效果
ctx.beginPath()
// 順時針繪制一個圓
ctx.arc(400, 400, 300, 0, Math.PI * 2, false)
// 逆時針繪制一個圓
ctx.arc(400, 400, 300, 0, Math.PI * 2, true)
#中心部分因為零和原則颤专,中間部分將不填充
ctx.closePath()
// 添加陰影效果
ctx.shadowColor = 'black'
ctx.shadowOffsetX = 2
ctx.shadowOffsetY = 2
ctx.shadowBlur = 4
ctx.fillStyle = '#058'
ctx.fill()
最終效果:
五. clearRect, isPointInPath##
clearRect(x, y, width, height)
清除指定寬高范圍內(nèi)的畫布,多用于動畫重新繪制新的圖案
ctx.clearRect(200, 200, canvas.weight, canvas.heigth)
isPointInPath(x, y)
判斷一個點是否在某個區(qū)域內(nèi)
一般獲取鼠標(biāo)在canvas中的位置使用:
var x = event.clientX - canvas.getBoundingClientRect().left;
var y = event.clientY - canvas.getBoundingClientRect().top;
// (x, y)即為鼠標(biāo)所在canvas的坐標(biāo)
然后通過 isPointInPath(x, y)來判斷是否在所選區(qū)域內(nèi)
示例:
當(dāng)鼠標(biāo)在區(qū)域內(nèi)時钠乏,點擊小球改變顏色:
var canvas = document.getElementById('#canvas')
canvas.height = 800
canvas.width = 800
var ctx = canvas.getContext('2d')
// 創(chuàng)建一個容器,用來放置所有小球的信息
var balls = []
window.onload = function() {
// 產(chǎn)生10個隨機(jī)球
for (var i = 0; i < 10; i++) {
var iBall = {
x: Math.random() * canvas.width,
y: Math.random() * canvas.heigth,
r: Math.random() * 20 + 20
}
balls[i] = iBall
}
draw()
canvas.addEventListener('click', detect)
}
// 獲取隨機(jī)顏色值
function getRandomColor() {
return '#' + ('00000' +Math.random() * 0x1000000<<2).toString(16).slice(-6)
}
function draw() {
for (var i = 0; i < balls.length; i++) {
ctx.beginPath();
ctx.arc(balls[i].x, balls[i].y, balls[i].r, 0, Math.PI * 2);
ctx.fillStyle = '#058';
ctx.fill();
}
}
function detect(e) {
var x = event.clientX - canvas.getBoundingClientRect().left;
var y = event.clientY - canvas.getBoundingClientRect().top;
ctx.beginPath();
ctx.arc(balls[i].x, balls[i].y, balls[i].r, 0, Math.PI * 2);
# 判斷鼠標(biāo)位置,是否在圓內(nèi)
ctx.fillStyle = getRandomColor() || 'red';
ctx.fill();
}