これまでの問題はパス グラフィックの描画に関するものでした。このセクションでは、グラフィックでは美しい女の子を描くことができないため、どうすればよいかについて説明します。美しい女の子を描きたいですか?フォローしてください:
キャンバスに画像を挿入したい場合、必要なメソッドはこのヒーローです:
drawImage() キャンバス上に画像、キャンバス、またはビデオを描画します
このヒーローは非常に強力な武道家です。多くのユニークなスキルと深い精神力を備えた、マスター中のマスターと呼ぶにふさわしい、そのユニークなスキルを見てみましょう:
秘伝書 1:
drawImage(img,x,y) 上に画像を配置します。 Canvas
パラメータ: img はインポートされた画像オブジェクトを表し、x、y はインポートされた座標を表します
ps: ここでの img は画像のパスではなく画像のオブジェクトを表します。つまり、画像を直接参照することはできませんが、参照する必要があります。 DOM を操作することで参照できます
上記のヒントは写真の特殊性について述べていますが、ではどうやって写真を取得するのでしょうか?最も単純なものから始めましょう:
<canvas width="500" height="400" id="canvas"> <span>亲,您的浏览器不支持canvas,换个浏览器试试吧!</span> </canvas> <img src="2.jpg" alt="" id="img">
ページに写真を載せて(あの頃のミルクティーガール、ああ、あの頃)、それからこの写真を取得してキャンバスに置きます
var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var oImg = document.getElementById("img"); ctx.drawImage(oImg,10,10);
ほら、キャンバスの外にいるミルクティーガールは私たちのキャンバスに行ってしまったのですが、残念なことに、現在のミルクティーガールはもう現在のミルクティーガールではありません。私のミルクティーガールを私のキャンバスに入れたいだけだと言う人もいます。 (なぜ彼女は私のミルクティーガールなのですか)彼女を他の場所で見られたくないのですが、どうすればよいですか?賢い生徒なら気づいたはずだけど、写真を外に隠しておけばいいんじゃないの?隠すだけで大丈夫ですか?
<img src="2.jpg" alt="" id="img" style="display:none;">
私のキャンバスにはミルクティーガールだけが残っています。 はい、これが私のページにあるということです。画像を表示するかどうかについては、この DOM 要素を取得するだけで十分です。画像を非表示にする方法は、p に入れる、p に隠す、などさまざまです。 Canvas タグに入れてください (Canvas をサポートしているブラウザには表示されないものがあるため) など、純粋な JS を使用して画像を追加することもできます:
var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var aImg = new Image(); aImg.src = '2.jpg'; ctx.onload = function(){ ctx.drawImage(aImg,10,10,400,300); }
秘密のヒント 2:
drawImage (img,x,y,w,h) キャンバス上に画像を配置し、画像の幅と高さを指定します
パラメータ: img はインポートされた画像オブジェクトを表し、x、y はインポートされた座標を表し、w、h は表します指定された絵のサイズ
ミルクティーガールは良いと言う生徒もいますが、私のキャンバスは小さすぎます、ミルクティーガールは大きすぎます(どこが大きすぎるのですか)、絵は大きすぎます、基本的に私のキャンバスをいっぱいにしますキャンバスに他のグラフィックを描いたら、ミルクティーガールの体が小さくなってしまいませんか?はは、drawImage には孫悟空の変身能力があります。信じられない方は、こちらをご覧ください:
おっと、上のミルクティーの女の子が突然小さくなりました。 、小さすぎてよく見えません、そして他の悪いクラスメートではなくミルクティーの女の子だけが見たいのですが大丈夫ですか?もちろんできます
秘密のヒント3
drawImage(img,sX,sY,sWidth,sHeight,x,y,width,height) キャンバスを切り取り、切り取った部分をキャンバス上に配置します
パラメータ: img は導入された画像オブジェクトを表します、sX、sYはクリッピングの実際の座標を表します、sWidth、sHeightはクリッピングの幅と高さを表します、x、yはキャンバス上に画像を配置する座標を表します、width、heightは画像の幅と高さを表します上記のパラメータはたくさんありますが、それでもわかりやすいようにミルクティーガールを切り出す方法を見てみましょう:
var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var oImg = document.getElementById("img"); ctx.drawImage(oImg,10,10,200,150);
ナリ、ミルクティーガールはどうやって太ったのですか? ?パラメータをもう一度見てみると、この時点での最後の幅と高さは、前の w と h とは異なる意味を持っていることがわかりました。これらは、トリミングされた画像がトリミングされた幅と高さよりも大きい場合に表示する必要があるサイズを示しています。 , 引っ張られて、切り取られた幅と高さよりも小さい場合は縮小されます。それでは、切り取ったサイズと同じサイズに調整して見てみましょう:
var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var oImg = document.getElementById("img"); ctx.drawImage(oImg,150,0,150,300,10,10,400,300);
はい、そうです。次のxとyがトリミングされたサイズを表しています。写真に表示されている位置を中央に調整して見てください。
ctx.drawImage(oImg,150,0,150,300,150,10,150,300);
所以在使用裁剪功能的时候,要特别注意,图片的坐标和宽高和原来的意思有区别了
秘籍四
drawImage(video,x,y,w,h) 在画布上定位视频,并规定视频的宽高
参数:video表示引入是视频对象 ,x,y表示视频引入的坐标 ,w,h表示规定视频的大小
其实这里跟引入图片是一样的,只不过它是可以引入视频的,但是这里的引入只是视频的某一帧,所以,当我们直接引入时,是没有什么反应的:
<canvas width="500" height="400" id="canvas"> <span>亲,您的浏览器不支持canvas,换个浏览器试试吧!</span> </canvas> <video id="video1" controls width="270"> <source src="http://www.w3school.com.cn/example/html5/mov_bbb.mp4" type='video/mp4'> <source src="http://www.w3school.com.cn/example/html5/mov_bbb.ogg" type='video/ogg'> <source src="http://www.w3school.com.cn/example/html5/mov_bbb.webm" type='video/webm'> </video>
var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var oVideo = document.getElementById("video1"); ctx.drawImage(oVideo,10,10,270,135);
当视频设置为autoplay时:
可以看到,右侧视频在播放,左侧没有什么反应,那怎么来播放视频呢?我们可以加一个定时器,然后每隔20秒执行一次绘画,这样一帧一帧的执行,连续起来就是一个视频了,其实视频的原理也是如此:
var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var oImg = document.getElementById("img"); var oVideo = document.getElementById("video1"); var timer = setInterval(function(){ ctx.drawImage(oVideo,10,10,270,135); },20)
不想让画布外的视频显示的方法跟图片是一样的,这里我就不细说了
除了drawImage具有裁剪功能外,还有一个方法可以裁剪——clip(),不同的是drawImage是裁剪图片,而clip只能裁剪图形,我们具体来看一下clip吧:
clip() 从原始画布中剪切任意形状和尺寸
ps:一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内(不能访问画布上的其他区域)。您也可以在使用 clip() 方法前通过使用 save() 方法对当前画布区域进行保存,并在以后的任意时间对其进行恢复(通过 restore() 方法)。
提示这里很重要,一定要看清楚,就好比是一张很大的油画布,现在我们用一个木框架子框一部分,然后就只能在这个框里画画了,框外面的就不能来画,恩,就是这个意思,当我们用restore()方法恢复画布是时候,就好比是把木框架子取掉了,这样就可以在别的地方画了,当然画好的这部分就不要动了
举个栗子:
不用clip裁剪:
ctx.arc(200, 200, 80, (Math.PI/180)*0, (Math.PI/180)*360, false); ctx.fill(); ctx.beginPath(); ctx.fillStyle = 'green'; ctx.arc(150, 150, 100, (Math.PI/180)*0, (Math.PI/180)*360, false); ctx.fill();
使用clip裁剪:
ctx.arc(200, 200, 80, (Math.PI/180)*0, (Math.PI/180)*360, false); ctx.clip(); ctx.fill(); ctx.beginPath(); ctx.fillStyle = 'green'; ctx.arc(150, 150, 100, (Math.PI/180)*0, (Math.PI/180)*360, false); ctx.fill();
可以比较看出,后面画的圆只显示在裁剪的圆的范围中,其他的都不见了,再看一个例子:
ctx.fillRect(50,50,200,100); ctx.clip(); ctx.beginPath(); ctx.fillStyle = 'green'; ctx.fillRect(100,100,200,100);
我们将圆改成实心的矩形,且是用的fillRect,而不是用fill,rect,结果是:
只显示第一个矩形,第二个矩形貌似不见了,而我们分开写:
//不用clip裁剪 ctx.rect(50,50,200,100); ctx.fill(); ctx.beginPath(); ctx.fillStyle = 'green'; ctx.fillRect(100,100,200,100);
//使用clip裁剪 ctx.rect(50,50,200,100); ctx.fill(); ctx.clip(); ctx.beginPath(); ctx.fillStyle = 'green'; ctx.fillRect(100,100,200,100);
//第一个矩形不填充,只画路径 ctx.rect(50,50,200,100); ctx.clip(); ctx.beginPath(); ctx.fillStyle = 'green'; ctx.fillRect(100,100,200,100);
//将clip放在第二个矩形 ctx.rect(50,50,200,100); ctx.fill(); ctx.beginPath(); ctx.fillStyle = 'green'; ctx.rect(100,100,200,100); ctx.clip(); ctx.fill();
上面的4张图分别代码上面的4段代码的效果,从这4组对比我们可以看出:
第一:裁剪的路径必须在被裁剪图形的前面申明,如果在后面申明,则会没有效果或只有第一个图形显示;
第二:如果前面的裁剪区域要是没有填充的话,显示的部分就要2图形相交的部分,但是绘图区域还是裁剪部分的区域
下面要讲的是像素操作,相对来说比较复杂,需要大家的强大大脑来脑补和理解,何为像素操作?我们说任何在屏幕上显示的图形,图片,画面等等都是由像素构成的,最好理解的就是户外大屏幕的画面了,因为户外大屏幕分辨率比较小,相比之下像素的颗粒就比较大,可以清晰的看到画面是又密密麻麻的像素来显示的,当户外大屏幕的屏幕有部分花了的话,会看到呈现的画面就会在花掉的部分显示成别的颜色,很明显,对于我们的电脑显示器也是一个原理!
那么像素是通过什么来获取的呢?我们来一一介绍:
getImageData(x,y,w,h) 拷贝画布指定矩形的像素数据
参数:x,y表示开始复制的左上角的坐标 w,h表示将要复制的区域大小
参数很好理解,就是在什么地方复制多大一个地方,而且它返回一个ImageData对象,ImageData对象里面包含了几个参数:
data:数组集合 width :对象的宽度 height:对象的高度
后面2个好理解,这个data集合到底是什么呢?我们来举个栗子:
ctx.fillRect(0,0,100,100); //获取矩形框的像素 var oImg = ctx.getImageData(0,0,100,100); console.log(oImg); console.log(oImg.data) console.log(oImg.data.length)
我们画了一个100*100的实现矩形,然后获取它的像素,然后分别查看了他们代表的意思,可以看到一个很有意思的东西,就是100*100的矩形的像素点应该是10000个像素,为什么它的长度是40000,这个我们可以在data数组中可以看到端倪,一个像素点是由rgba构成的:
r : 0-255 黑-白
g : 0-255 黑-白
b : 0-255 黑-白
a : 0-255 透明-不透明
也就是说,一个像素是用这4个值组成的,那么10000个像素的长度自然就是40000了,而且对应的值在console中也能看到,好了,理解了这一点,那么就里像素操作不远了!
除了我们在已知的地方去获取像素外,我们能不能自己来创造像素区域呢?答案是肯定的:
createImageData(w,h) 表示创建一个规定尺寸的ImageData对象
createImageData(ImageData) 表示创建与指定的另一个 ImageData 对象尺寸相同的新 ImageData 对象(不会复制图像数据)
我们也来写一个例子:
var oImg = ctx.createImageData(100,100); console.log(oImg); console.log(oImg.data) console.log(oImg.data.length)
恩,跟前面的是一样的,创建了一个ImageData对象,当然画布是没有任何的效果的,因为我们只是创建了一个对象,那么我们怎么将我们创建的对象放到画布上呢?那就要用到这个方法:
putImageData(imageData,x,y,dirtyX,dirtyY,dirtyWidth,dirtyHeight) 方法将图像数据(从指定的 ImageData 对象)放回画布上
参数:
imageData指创建或获取的imageData对象
x,y表示imageData对象的左上角坐标
dirtyX,dirtyY 表示在画布上放置图像的坐标,以像素计,可选
dirtyWidth,dirtyHeight 表示在画布上放置的图形大小,可选
我们也举2个例子:
ctx.fillRect(0,0,100,100); //获取矩形框的像素 var oImg = ctx.getImageData(0,0,100,100); ctx.putImageData(oImg,100,100);
ctx.fillRect(0,0,100,100); //获取矩形框的像素 var oImg = ctx.getImageData(0,0,100,100); ctx.putImageData(oImg,100,100); ctx.putImageData(oImg,100,0,10,10,50,50);
看,这么一对比,就知道这些参数是什么意思了,其中第二个我还得解释一下,如图小矩形,x,y依然是表示大的ImageData对象的起始坐标,dirtyX,dirtyY,dirtyWidth,dirtyHeight表示是相对于现在的x,y的起始坐标再来定义位置和大小,这么解释应该就明白了!
这就是操作像素的方法,看着很简单,也挺好理解,但是真正用起来,还是蛮费脑子的,我们先来看第一个小实例,改变像素的颜色:
实例效果,改变100*100的黑色矩形框中的第10行第10列的颜色,颜色为红色,先画个图分析一下:
从上面的console中也可以看出,像素点是从0开始的,如果上图就是矩形的像素点的分布(画的有点烂,将就看一下),第10行,也就是第11行,第10列也就是第11列,那么前十列的像素就是矩形的宽度*10,剩下的就是11个像素,二者相加就是所有的像素,然后每个像素用4个值表示,就是总和乘以4,好具体我们看代码:
ctx.fillRect(0,0,100,100); //获取矩形框的像素 var oImg = ctx.getImageData(0,0,100,100); setXY(oImg,10,10,[255,0,0,255]); //设置目标像素点的颜色 function setXY(obj,x,y,color){ var w = obj.width; var h = obj.height; obj.data[4*(w*y+x)] = color[0]; obj.data[4*(w*y+x)+1] = color[1]; obj.data[4*(w*y+x)+2] = color[2]; obj.data[4*(w*y+x)+3] = color[3]; } ctx.putImageData(oImg,100,100);
看,(10,10)坐标点的颜色就变成了红色,一个点还是不太明显,如果是一条横向的或者的纵向的,就跟明显了:
ctx.fillRect(0,0,100,100); //获取矩形框的像素 var oImg = ctx.getImageData(0,0,100,100); for(var i=0;i<oImg.width;i++){ setXY(oImg,i,10,[255,0,0,255]); } for(var i=0;i<oImg.height;i++){ setXY(oImg,10,i,[255,0,0,255]); } //设置目标像素点的颜色 function setXY(obj,x,y,color){ var w = obj.width; var h = obj.height; obj.data[4*(w*y+x)] = color[0]; obj.data[4*(w*y+x)+1] = color[1]; obj.data[4*(w*y+x)+2] = color[2]; obj.data[4*(w*y+x)+3] = color[3]; } ctx.putImageData(oImg,100,100);
我们经常看到那种很炫酷的粒子文字,图形的效果,既然我们可以操作像素,如果我们只显示一部分的像素点,不就是粒子效果吗?看下面的例子:
ctx.fillStyle = 'red'; ctx.arc(150,150,100,0,360*Math.PI/180,false); ctx.fill(); //获取矩形框的像素 var oImg = ctx.getImageData(50,50,200,200); ctx.clearRect(0,0,canvas.width,canvas.height); var w = oImg.width; var h = oImg.height; var arr = randomNum(w*h,w*h/10); var newImg = ctx.createImageData(oImg); for(var i=0;i<arr.length;i++){ newImg.data[4*arr[i]] = oImg.data[4*arr[i]]; newImg.data[4*arr[i]+1] = oImg.data[4*arr[i]+1]; newImg.data[4*arr[i]+2] = oImg.data[4*arr[i]+2]; newImg.data[4*arr[i]+3] = oImg.data[4*arr[i]+3]; } ctx.putImageData(newImg,50,50); //某区域的部分随机数 function randomNum(iAll,iNow){ var arr = []; var newArr = []; for(var i=1;i<=iAll;i++){ arr.push(i); } for(var i=0;i<iNow;i++){ newArr.push(arr.splice(Math.random()*(arr.length),1)); } return newArr; }
上面的例子表示,我只显示当前的十分之一的像素,具体效果看这里——粒子图形
像素操作不仅仅只能操作图形,图片我们一样可以操作,这样我们就能做出更多的效果,看看下面的例子,我们将图像换成图片,看看能不能做出粒子图片:
var aImg = new Image(); aImg.src = '3.jpg'; //当图片加载完毕才能进行下面的操作 aImg.onload = function(){ draw(this); } function draw(obj){ ctx.drawImage(obj,0,0,400,400); //获取矩形框的像素 var oImg = ctx.getImageData(0,0,400,400); var w = oImg.width; var h = oImg.height; var arr = randomNum(w*h,w*h/2); var newImg = ctx.createImageData(oImg); for(var i=0;i<arr.length;i++){ newImg.data[4*arr[i]] = oImg.data[4*arr[i]]; newImg.data[4*arr[i]+1] = oImg.data[4*arr[i]+1]; newImg.data[4*arr[i]+2] = oImg.data[4*arr[i]+2]; newImg.data[4*arr[i]+3] = oImg.data[4*arr[i]+3]; } ctx.putImageData(newImg,0,0); } //某区域的部分随机数 function randomNum(iAll,iNow){ var arr = []; var newArr = []; for(var i=1;i<=iAll;i++){ arr.push(i); } for(var i=0;i<iNow;i++){ newArr.push(arr.splice(Math.random()*(arr.length),1)); } return newArr; }
这里我用的是二分之一的像素点,十分之一太少了,看得不明显,由此我们可以知道,也是可以来操作图片的像素点的,具体效果看这里 —— 图片粒子化
既然我们可以操作图片,那可以做的事就太多了,先看看下面这个例子,图片反色(即将原图片的像素颜色用相反的颜色替代):
var aImg = new Image(); aImg.src = '1.jpg'; aImg.onload = function(){ draw(this); } function draw(obj){ ctx.drawImage(obj,0,0,400,400); var oImg = ctx.getImageData(0,0,400,400); var w = oImg.width; var h = oImg.height; for(var i=0;i<h;i++){ for(var j=0;j<w;j++){ var result = []; var color = getXY(oImg,j,i); result[0] = 255 - color[0]; result[1] = 255 - color[1]; result[2] = 255 - color[2]; result[3] = 255; setXY(oImg,j,i,result); } } ctx.putImageData(oImg,w,0); } function getXY(obj,x,y){ var w = obj.width; var h = obj.height; var d = obj.data; var color = []; color[0] = obj.data[4*(y*w+x)]; color[1] = obj.data[4*(y*w+x)+1]; color[2] = obj.data[4*(y*w+x)+2]; color[3] = obj.data[4*(y*w+x)+3]; return color; } function setXY(obj,x,y,color){ var w = obj.width; var h = obj.height; var d = obj.data; obj.data[4*(y*w+x)] = color[0]; obj.data[4*(y*w+x)+1] = color[1]; obj.data[4*(y*w+x)+2] = color[2]; obj.data[4*(y*w+x)+3] = color[3]; }
是不是很有意思,具体效果可以看这里—— canvas图片反色
用这个例子,我们还可以延伸一下,让它反色后,我们再加一个倒影,美女不明显,我们用山峰那张:
var aImg = new Image(); aImg.src = '3.jpg'; aImg.onload = function(){ draw(this); } function draw(obj){ ctx.drawImage(obj,0,0,obj.width,obj.height); var oImg = ctx.getImageData(0,0,obj.width,obj.height); var w = oImg.width; var h = oImg.height; var newImg = ctx.createImageData(obj.width,obj.height) for(var i=0;i<h;i++){ for(var j=0;j<w;j++){ var result = []; var color = getXY(oImg,j,i); result[0] = 255 - color[0]; result[1] = 255 - color[1]; result[2] = 255 - color[2]; result[3] = 255; setXY(newImg,j,h-i,result); } } ctx.putImageData(newImg,0,h); } function getXY(obj,x,y){ var w = obj.width; var h = obj.height; var d = obj.data; var color = []; color[0] = obj.data[4*(y*w+x)]; color[1] = obj.data[4*(y*w+x)+1]; color[2] = obj.data[4*(y*w+x)+2]; color[3] = obj.data[4*(y*w+x)+3]; return color; } function setXY(obj,x,y,color){ var w = obj.width; var h = obj.height; var d = obj.data; obj.data[4*(y*w+x)] = color[0]; obj.data[4*(y*w+x)+1] = color[1]; obj.data[4*(y*w+x)+2] = color[2]; obj.data[4*(y*w+x)+3] = color[3]; }
细心的同学一定能看出它的原理,没错,就是在设置像素的时候,将它的像素方向反向,即此处“h-i”,具体效果看这里——canvas图片反色倒影
我们还可以做一些延伸,我们可以通过像素的透明度的参数,给它设置投影渐变,我们来看看:
var aImg = new Image(); aImg.src = '3.jpg'; aImg.onload = function(){ draw(this); } function draw(obj){ ctx.drawImage(obj,0,0,obj.width,obj.height); var oImg = ctx.getImageData(0,0,obj.width,obj.height); var w = oImg.width; var h = oImg.height; var newImg = ctx.createImageData(obj.width,obj.height); for(var i=0;i<h;i++){ for(var j=0;j<w;j++){ var result = []; var color = getXY(oImg,j,i); result[0] = 255 - color[0]; result[1] = 255 - color[1]; result[2] = 255 - color[2]; result[3] = 255*i/h; setXY(newImg,j,h-i,result); } } ctx.putImageData(newImg,0,h); } function getXY(obj,x,y){ var w = obj.width; var h = obj.height; var d = obj.data; var color = []; color[0] = obj.data[4*(y*w+x)]; color[1] = obj.data[4*(y*w+x)+1]; color[2] = obj.data[4*(y*w+x)+2]; color[3] = obj.data[4*(y*w+x)+3]; return color; } function setXY(obj,x,y,color){ var w = obj.width; var h = obj.height; var d = obj.data; obj.data[4*(y*w+x)] = color[0]; obj.data[4*(y*w+x)+1] = color[1]; obj.data[4*(y*w+x)+2] = color[2]; obj.data[4*(y*w+x)+3] = color[3]; }
核心代码就一个地方,255*i/h 给透明度乘以一个百分比的值,从0-255,也是很巧妙啊,具体效果看这里——canvas图片反色投影渐变
总结一下吧:虽然像素操作的方法不多,但是其功能之强大,是很多人都要望其项背的,关键在于人的想象力和深入的理解,要理解这样一个概念,掌握了像素操作,就好比是掌握了物体的核心本质,你就是造物者,你想造出什么东西,全凭你的想法,这里只是展示了一个很低劣的效果,旨在给大家展示一下canvas像素操作的魅力,抛砖引玉而已,后期会单独发一下canvas的像素操作的实例给大家参考,这个东西实在是太重要了!
好了,弄了几天了,终于写完了,本来想多写一点的,想想,后面再扩展吧!谢谢大家!
以上就是canvas API ,通俗的canvas基础知识(五) 的内容,更多相关内容请关注PHP中文网(www.php.cn)!