一、圖形的組合方式
globalAlpha是一個介于0和1之間的值(包括0和1),用于指定所有繪制的透明度。默認值為0。如果所有后續操作都要基于相同的透明度,就可以先把globalAlpha設置為適當值,然后繪制,最后在把它設置回默認值0.
//繪制一個紅色的矩形
context.fillStyle="red";
context.fillRect(10,10,50,50);
//修改全局透明度
context.globalAlpha=0.5;
//繪制藍色矩形
context.fillStyle="blue";
context.fillRect(30,30,50,50);
//重置全局透明度
context.globalAlpha=0;
//這個例子中藍色矩形會呈現半透明效果,透過它可以看到下面的紅色矩形。
globalCompositionOperation表示繪制后的圖形怎樣與先繪制的圖形結合。這個屬性的值是字符串,可能的值如下:
source-over(默認值):后繪制的圖形位于先繪制的圖形上方。
source-in:后繪制的圖形與先繪制的圖形重疊的部分可見,兩者其他部分完全透明。
source-out:后繪制的圖形與先繪制的圖形不重疊的部分可見,先繪制的圖形完全透明。
source-atop:后繪制的圖形與先繪制的圖形重疊的部分可見,先繪制圖形不受影響。
destination-over:后繪制的圖形位于先繪制的圖形下方,后繪制的圖形只有之前透明像素下的部分才可見,被遮擋的部分不可見。
destination-in:后繪制的圖形位于先繪制的圖形下方,兩者不重疊的部分完全透明。就是顯示重疊的部分的形狀,顯示先繪制的圖形的顏色。
destination-out:后繪制的圖形擦除與先繪制的圖形重疊的部分。
destination-atop:后繪制的圖形位于先繪制的圖形下方,在兩者不重疊的地方,先繪制的圖形會變透明。
lighter:后繪制的圖形與先繪制的圖形重疊部分的值相加,使該部分變亮。(疊加的是顏色額)
copy:后繪制的圖形完全替代與之重疊的先繪制圖形。
xor:后繪制的圖形與先繪制的圖形重疊的部分執行“異或”操作;圖像異或,相異為1,相同為0.即重疊部分不顯示,不重疊部分顯示。
//畫目標圖
context.beginPath();
context.fillStyle="red";
context.fillRect(100,50,100,300);
//畫源圖
context.beginPath();
context.globalCompositeOperation="destination-out";
//context.globalCompositeOperation="xor";
context.fillStyle="blue";
context.arc(250,200,100,0, Math.PI*2);
目標圖像:已經放到畫布中的繪圖
源圖像:打算放置到畫布中的繪圖
在使用globalCompositionOperation的情況下一定要多測一些瀏覽器,因為不同的瀏覽器對這個屬性的實現仍然存在較大的差別。
二、使用圖片
//第一種寫法:第一個參數:圖片對象;第二個參數是開始畫的x坐標;第三個參數:開始畫的y的坐標;
//context.drawImage(img,0,0);
//第二種寫法:第一個參數:圖片對象;第二個參數:是開始畫的x坐標;第三個參數:開始畫的y的坐標;第四個參數:圖片繪制到canvas上的寬度;第五個參數:圖片繪制到canvas上的高度;
//context.drawImage(img,0,0,200,200);
//第三種寫法(裁剪):第一個參數:圖片對象;第二三個參數在原圖上要裁剪的起始x、y的坐標;第四、五個參數:在原圖上要裁剪的寬和高;第六、七個參數:要放到canvas畫布上的起始x、y坐標;第八九個參數:要到canvas畫布上的寬和高;
context.drawImage(img,100,100,100,100,50,50,300,300);
var ?canvas=document.getElementById("myCanvas");
var ? context=canvas.getContext("2d");
var ? img= newImage();
img.src="img/20150821130732_anQeR.jpeg";
img.onload=function(){
context.drawImage(img,100,100,100,100,50,50,300,300);
}
綜合使用drawImage( )和其他方法,可以對圖像進行各種基本操作。而操作的結果可以通過toDataURL( )方法獲得。不過有一個例外,即圖像不能來自其他域。如果圖像來自其它域,調用toDataURL( )會拋出錯誤。
canvas提供了toDataURL的接口,可以方便的將canvas畫布轉化成base64編碼的image。目前支持的最好的是png格式,jpeg格式的現代瀏覽器基本也支持,但是支持的不是很好。
//我們創建一個畫布,在上面做一些繪圖,然后試著把它保存為本地圖片。
var ?can = document.getElementById('canvas');
var ?ctx = can.getContext('2d');
var ? imgA = new Image();
imgA.src ='http://www.html5party.com/wp-content
/uploads/2013/09/css_yangshijiance.png';
imgA.onload = function() {
ctx.drawImage(imgA, -25, 0, imgA.width, imgA.height);
ctx.restore();
};
var ? imgB = new Image();
imgB.src = 'http://www.html5party.com/wp-content
/uploads/2013/09/canvas_chroma.png';
imgB.onload = function() {
ctx.globalAlpha = 0.1
ctx.drawImage(imgB, -100, -75, imgB.width, imgB.height);
ctx.restore();
};
function toImage(returnType) {
var dataURL = document.getElementById('canvas').toDataURL("image/png");
// The returnType argument specifies how to get the
// the image. ?'obj' will set the source to an image element.
// 'window' will open a new window and display the image.
// 'download' will prompt the user to save the image.
switch(returnType) {
case 'obj':
var imgObj = new Image();
imgObj.src = dataURL;
document.getElementById('graphics').appendChild(imgObj);
break;
case 'window':
window.open(dataURL, "Canvas Image");
break;
case 'download':
dataURL = dataURL.replace("image/png", "image/octet-stream");
document.location.href = dataURL;
break;
}
}
理想的狀態是頁面上有三個點擊鏈接:
1、Image Tag
點擊會有一張圖片插入在網頁中。
2、New Window
打開一個新的窗口,并顯示一個PNG格式的圖片
3、Download
下載保存一張PNG個格式的圖片
很不幸,點擊并沒有反映,為什么呢?筆者做了許多試驗,原因出在圖片渲染上,canvas上如果添加圖片,toDataURL()將失效,獲取不到canvas信息。通過簡單幾步來驗證這個問題。
把js腳本中圖片渲染的部分去除,A和B圖。為了便于觀察,在畫布上畫一個簡單的矩形,填充顏色:
ctx.fillStyle = 'Red';
ctx.strokeStyle = 'Green';
ctx.beginPath();
ctx.rect(20, 20, 100, 100);
ctx.stroke();
ctx.fill();
這樣畫布上便顯示為一個紅色矩形,格式為PNG
三、裁剪
var ?canvas=document.getElementById("myCanvas");
var ?context=canvas.getContext("2d");
//繪制一個三角形
context.beginPath();
context.moveTo(200,200);
context.lineTo(250,200);
context.lineTo(225,150);
context.closePath();
context.stroke();
//調用裁剪
context.clip();
var ?img= newImage();
img.src="img/20150821130732_anQeR.jpeg";
img.onload=function() {
context.drawImage(img,0,0);
}
//你上面畫的是什么形狀,裁剪出來的就是什么形狀
四、使用圖像數據
var ?canvas=document.getElementById("myCanvas");
var ? context=canvas.getContext("2d");
//getImageData()取的原始圖像數據
//有四個參數:第一二個參數,開始獲取像素的x,y坐標,第三四個參數,獲取像素點的寬高。
//var? ImageData=context.getImageData(0,0, img.width, img.height);
這里返回的對象是getImageData()的實例,每個getImageData()對象都有三個屬性:width、height、data。
width和height表示訪問像素區域大小
//其中data屬性是一個數組,保存著圖像中每一個像素的數據。
//在data數據中,每一個像素用4個元素來保存,分別代表紅、綠、藍和透明度。
//數組中的每個元素都是介于0和255之間的,能夠直接訪問到原始圖像數據,
//就能夠以各種方式來操作這些數據。
var ?img= newImage();
img.src="img/20150821130732_anQeR.jpeg";
img.onload=function() {
context.drawImage(img,0,0);
var ? ImageData=context.getImageData(0,0, img.width, img.height);
var ? data=ImageData.data;
//注意:每次循環控制變量i都遞增4,在取得每個像素的紅、綠、藍顏色值后,
//計算出他們的平均值,再把這個平均值設置為每個顏色的值,
//結果就是去掉了每個像素的顏色,只保留了亮度接近的灰度值.(用彩色變黑白)
for(var i=0; i<data.length; i+=4){
var ?r=data[i];
var ?g=data[i+1];
var ?b=data[i+2];
//求rgb的平均值,
var ?gray=(r+g+b)/2;
//設置顏色值
data[i]=gray;
data[i+1]=gray;
data[i+2]=gray;
}
//putImageData()方法把圖像數據繪制到畫布上,有三個參數
//第一個參數,獲取ImageData,第二三個參數,要放到畫布上的x,y坐標
context.putImageData(ImageData,0,0);
}
五、canvas視頻處理
function animation( ){
//video.ended 屬性返回音頻/視頻是否已結束。返回值:布爾值true|false。如果播放已結束,則返回 true。否則返回 false。
if(!video.ended){
//將視頻的每一幀,繪制在canvas上
context.drawImage(video,0,0,canvas.width,canvas.height);
window.requestAnimationFrame(animate);
}
}
//當視頻可以播放時,進行播放調用循環
//video的oncanplay事件類似于圖像的onload,加載完成之后
//oncanplay 事件在用戶可以開始播放視頻/音頻(audio/video)時觸發。
//object.oncanplay=function(){myScript};
//object.addEventListener("canplay",myScript);
video.oncanplay=function(e){
video.play( );
window.requestAnimationFrame(animate);
}
六、canvas動畫
最原始的你還可以使用window.setTimout()或者window.setInterval()通過不斷更新元素的狀態位置等來實現動畫,前提是畫面的更新頻率要達到每秒60次才能讓肉眼看到流暢的動畫效果。
window.requestAnimationFrame()通過遞歸調用同一方法來不斷更新畫面以達到動起來的效果,但它優于setTimeout/setInterval的地方在于它是由瀏覽器專門為動畫提供的API,在運行時瀏覽器會自動優化方法的調用,并且如果頁面不是激活狀態下的話,動畫會自動暫停,有效節省了CPU開銷。
取消該次動畫:可以直接調用,也可以通過window來調用,接收一個函數作為回調,返回一個ID值,通過把這個ID值傳給window.cancelAnimationFrame()可以取消該次動畫。
window.requestAnimationFrame()方法用于告訴瀏覽器,你想在瀏覽器的下個重繪 之前來執行一個動畫或者執行瀏覽器通過調用一個特定的函數來更新動畫的請求。該方法會在下次重回之前執行一個作為參數的回調函數。
window.requestAnimationFrame 是專門為實現高性能的幀動畫而設計的一個API,目前已在多個瀏覽器得到了支持,包括IE10+,Firefox,Chrome,Safari,Opera等,在移動設備上,ios6以上版本以及IE mobile 10以上也支持requestAnimationFrame,唯一比較遺憾的是目前安卓上的原生瀏覽器并不支持requestAnimationFrame,不過對requestAnimationFrame的支持應該是大勢所趨了,安卓版本的chrome 16+也是支持requestAnimationFrame的。
例子window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
七、實現多個小球隨機運動,用面向對象的方法
var ? canvas=document.getElementById("myCanvas");
var ? context=canvas.getContext("2d");
//圓是一個類,就是對象只有一個,就是圓
functionCircle(x,y,r,speedX,speedY,color) {
//所有的屬性
this.r=r;
this.x=x;
this.speedX=speedX;
this.speedY=speedY;
this.y=y;
this.color=color;
}
//在原型上寫方法,
//第一個方法,畫圓
Circle.prototype.draw=function() {
context.beginPath();
context.fillStyle=this.color;
context.arc(this.x, this.y, this.r,0, Math.PI*2);
context.fill();
}
//第二個方法,運動
Circle.prototype.move=function() {
//這里改變x遞加的值,可以改變運動速度
this.x+=this.speedX;
this.y+=this.speedY;
if(this.x>=canvas.width-this.r||this.x<=this.r) {
this.speedX*= -1;
}
if(this.y>=canvas.height-this.r||this.y<=this.r) {
this.speedY*= -1;
}
}
//進行實例化操作
//實現同時出現多個小球,需要同時實例化出多個對象
//儲存new出來的實例化對象數組
var ? arr=[ ];
//來很多個小球
for(var ?i=0; i<100; i++) {
//設置一個隨機的半徑
var ? R=randomNum(30,5);
//隨機位置
var ? X=randomNum(canvas.width-R, R);
var ? ?Y=randomNum(canvas.height-R, R);
//隨機速度
var ? speedX=randomNum(8,1);
var ? ?speedY=randomNum(8,1);
//隨機顏色
var ? ? COLOR="rgb("+randomNum(255,0)+","+randomNum(255,0)+","+randomNum(255,0)+")";
//將對像實例化
var ? ?newCircle= newCircle(X, Y, R, speedX, speedY, COLOR);
arr.push(newCircle)
}
moveCircle();
//定義一個執行動畫的函數
function ? ?moveCircle() {
//先清理畫布
context.clearRect(0,0, canvas.width, canvas.height);
//循環執行實例化對象的數組
for(var ?i=0; i<arr.length;i++){
arr[i].draw();
arr[i].move();
}
//執行動畫
window.requestAnimationFrame(moveCircle);
}
//定義一個隨機數的函數
functionrandomNum(max,min) {
returnparseInt(Math.random()*(max-min+1)+min);
}