#7 高級canvas內(nèi)容:陰影, clip()等

本章主要介紹以下幾個方面:

  • 陰影效果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種网缝,還有更多的選項:

globalCompositeOperation 具體文檔

三.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();   
}

具體demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末春塌,一起剝皮案震驚了整個濱河市晓避,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌只壳,老刑警劉巖俏拱,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異吼句,居然都是意外死亡锅必,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門惕艳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搞隐,“玉大人,你說我怎么就攤上這事远搪×痈伲” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵谁鳍,是天一觀的道長癞季。 經(jīng)常有香客問我,道長倘潜,這世上最難降的妖魔是什么绷柒? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮涮因,結(jié)果婚禮上废睦,老公的妹妹穿的比我還像新娘。我一直安慰自己养泡,他們只是感情好郊楣,可當(dāng)我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布憔恳。 她就那樣靜靜地躺著,像睡著了一般净蚤。 火紅的嫁衣襯著肌膚如雪钥组。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天今瀑,我揣著相機(jī)與錄音程梦,去河邊找鬼。 笑死橘荠,一個胖子當(dāng)著我的面吹牛屿附,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播哥童,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼挺份,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了贮懈?” 一聲冷哼從身側(cè)響起匀泊,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎朵你,沒想到半個月后各聘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡抡医,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年躲因,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忌傻。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡大脉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出水孩,到底是詐尸還是另有隱情箱靴,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布荷愕,位于F島的核電站衡怀,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏安疗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一荐类、第九天 我趴在偏房一處隱蔽的房頂上張望怖现。 院中可真熱鬧,春花似錦、人聲如沸屈嗤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饶号。三九已至铁追,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間茫船,已是汗流浹背琅束。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留算谈,地道東北人涩禀。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像然眼,于是被迫代替她去往敵國和親艾船。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫高每、插件屿岂、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,059評論 4 62
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,749評論 25 707
  • 55歲之前想做的事,給自己定位,事業(yè)和生活. 事業(yè)上,先擁有屬于自己的茶行(雅玩茶舍).集成銷售各類茶品,紫砂壺及...
    雅玩閱讀 211評論 0 0
  • 今天是我實習(xí)的最后一天,早就想離開這里的我不知怎么心里卻有了一絲難受觉义,難道是對這里的不舍嗎?說實話這兩天真的有點傷...
    花仙子豬豬閱讀 239評論 0 1
  • 記得剛結(jié)婚那會磺浙,我和肥佬的做飯水平相當(dāng)洪囤,他的青菜炒得好,我煎魚還可以吧撕氧。后來慢慢地瘤缩,他學(xué)會了很多,做飯頗具...
    語歌晨唱閱讀 1,163評論 0 2