getContext 获取canvas绘图的上下文环境
之后在绘图环境上有以下API
lineJoin 线头线尾的形状 round为圆角
lineWidth 线的宽度
font 设置字体
fillText 填充文字 第一个参数是文本 第二个参数是X坐标,第三个参数是Y坐标
strokeRect 空心矩形,接收四个参数
fillStyle 填充样式 可以接受rgba或渐变
渐变例子
var gradient=can2_context.createLinearGradient(0,500,0,700);
gradient.addColorStop(0,'blue')
gradient.addColorStop(0.25,'white')
gradient.addColorStop(1,'blue')
can2_context.fillStyle=gradient;
can2_context.fillRect(0,500,canvas_2.width,700)
createLinearGradient的四个参数表示渐变的方向 0,500,0,700表示的是垂直方向渐变
addColorStop 第一个参数填0~1之间的数 第二个参数添颜色
fillRect 实心矩形
放射性渐变
这里用到了createRadialGradient这个API 这个API接收6个参数,前三个表示底下的圆,后三个表示上面的圆,返回的实例依然可以用addColorStop
function Radia(bottom_x,bottom_y,bottom_r,top_x,top_y,top_r){
this.bottom_x=bottom_x;
this.bottom_y=bottom_y;
this.bottom_r=bottom_r;
this.top_x=top_x;
this.top_y=top_y;
this.top_r=top_r;
this.gradient=can2_context.createRadialGradient(this.bottom_x,this.bottom_y,this.bottom_r,this.top_x,this.top_y,this.top_r)
}
Radia.prototype.addColor=function(){
for(var i=0;i<arguments.length;i++){
this.gradient.addColorStop(arguments[i].num,arguments[i].color)
}
}
Radia.prototype.draw=function(x1,y1,x2,y2){
can2_context.fillStyle=this.gradient;
can2_context.fillRect(x1,y1,x2,y2)
}
var some1=new Radia(canvas_2.width/2, canvas_2.height-100, 0, canvas_2.width/2, 0, 300)
some1.addColor({num:0.2,color:"blue"},{num:1,color:"yellow"},{num:0.7,color:"white"})
some1.draw(0, 0, canvas_2.width, canvas_2.height)
图像填充 createPattern
图像填充也是一种fillStyle
该方法的第一个参数指定了图案所用图像,它可以是images元素,canvas元素或者video元素.第2个参数告诉浏览器,对图形进行扫描或填充时,应该如何重复该图案,这个参数的有效值是repeat,repeat-x,repeat-y,no-repeat
var canvas_2=document.getElementById("canvas_2");
var can2_context=canvas_2.getContext("2d");
var img=new Image();
img.src='./images/hover2.png'
function fillimage(){
var pattern=can2_context.createPattern(img,'no-repeat')
can2_context.fillStyle=pattern;
can2_context.fillRect(0,0,canvas_2.width,canvas_2.height)
}
img.onload=function(e){
fillimage()
}
canvas阴影
尽量不要用canvas做出阴影 因为效率并不是那么高
shadowBlur表示阴影效果如何延伸double值。浏览器在阴影智商运用高斯模糊时,将会用到该值,它与像素无关,只会被用到高斯模糊方程之中,其默认值为0
shadowColor CSS格式的颜色字符串,默认值是rgba(0,0,0,0)
shadowOffsetX 阴影在X轴方向的偏移量,以像素为单位,默认值为0
shadowOffsetY 阴影在Y轴方向的偏移量,以像素为单位,默认值是0
var canvas_2=document.getElementById("canvas_2");
var can2_context=canvas_2.getContext("2d");
var SHADOW_COLOR='rgba(0,0,0,0.7)'
function setShadow(){
can2_context.shadowColor=SHADOW_COLOR;
can2_context.shadowOffsetX=3;
can2_context.shadowOffsetY=3;
can2_context.shadowBlur=5
}
setShadow();
can2_context.fillStyle="blue"
can2_context.fillRect(0,0,100,100)
关于beginPath
can2_context.beginPath();
can2_context.rect(10,10,100,100)
can2_context.stroke();
// can2_context.beginPath()
can2_context.rect(50,50,100,100)
can2_context.stroke()
先调用beginPath()来清楚当前路径中的所有子路径,然后调用rect()来创建一条包含矩形4个点的子路径,再调用stroke()方法使得这个矩形出现在canvas上。
接下来这段代码再次调用rect()方法,不过这一次,由于没有调用beginPath()方法清除原有的路径,所以第二次对rect()的方法的调用,会向当前中增加一条子路径。最后该段代码再一次调用stroke()方法。这次stroke()方法的调用,将会使得当前路径中的两条子路径都被描边,这意味着它会重绘第一个矩形
关于非零环绕原则
“非零环绕规则”是这么来判断有自我交叉情况的路径的:
对于路径中的任意给定区域,从该区域内部画一条足够长的线段
使此线段的终点完全落在路径范围之外。
接下来,将计数器初始化为0,然后,每当这条线段与路径上的直线或曲线相交时,就
改变计数器的值。如果是与路径顺时针部分相交,则加1,如果是与路径逆时针部分相交就
减一。若计数器的最终值不是0,那么此区域就在路径里面,在调用fill()方法时,浏览器就会对其进行填充。如果最终值是0,那么此区域就不在路径内部,浏览器也就不会对其进行填充
剪纸漏洞效果主要是利用了canvas的非零环绕规则
简单来说就是canvas fill填充的是同一路径顺时针和逆时针中间的部分,简称
非零环绕
var canvas_2=document.getElementById("canvas_2");
var can2_context=canvas_2.getContext("2d");
var width_2=canvas_2.width
var height_2=canvas_2.height
var testx=300
var testy=300
function drawCutouts(){
can2_context.beginPath();
addOurterRectanglePath()
addrect()
addTrianglePath()
addarc()
can2_context.fill()
}
function addOurterRectanglePath(){
can2_context.rect(110,25,370,335)
can2_context.closePath()
}
function rect(x,y,w,h,direction){
if(direction){
can2_context.moveTo(x,y);
can2_context.lineTo(x,y+h)
can2_context.lineTo(x+w,y+h)
can2_context.lineTo(x+w,y)
}else{
can2_context.moveTo(x,y)
can2_context.lineTo(x+w,y)
can2_context.lineTo(x+w,y+h)
can2_context.lineTo(x,y+h)
}
can2_context.closePath()
}
function addrect(){
rect(310,55,70,35,true) //true为逆时针
}
function addTrianglePath(){
can2_context.moveTo(400,200)
can2_context.lineTo(250,115)
can2_context.lineTo(200,200)
can2_context.closePath()
}
function addarc(){
if(testy>335){testy=25}
testy=testy+1
can2_context.arc(testx,testy,40,0,Math.PI*2,true)
}
function hi(){
can2_context.clearRect(0,0,width_2,height_2)
drawCutouts()
requestAnimationFrame(hi)
}
hi()
getBoundingClientRect,getImageData,putImageData
//getBoundingClientRect 返回元素的6个值 left top right bottom width height
注意这里的left top right bottom 都是相对于可视窗口的左上角的
width是元素的宽度
height是元素的高度
var bbox=canvas.getBoundingClientRect()
bbox.left
bbox.right
//getImageData() 方法返回 ImageData 对象,该对象拷贝了画布指定矩形的像素数据。
//context.getImageData(0,0,canvas.width,canvas.height)
//putImageData() 方法将图像数据(从指定的 ImageData 对象)放回画布上。
//context.putImageData(drawingSurfaceImageData,0,0)
canvas橡皮筋式线段绘制
context.save() 设置绘图环境存档
context.restore() 读取绘图环境存档
Math.abs()求绝对值
var canvas=document.getElementById("canvas"),
context=canvas.getContext("2d"),
eraseAllButton=document.getElementById("eraseAllButton"),
strokeStyleSelect=document.getElementById("strokeStyleSelect"),
guidewireCheckbox=document.getElementById("guidewireCheckbox"),
drawingSurfaceImageData,
mousedown={},
rubberbandRect={},
dragging=false,
guidewires=guidewireCheckbox.checked;
function windowToCanvas(x,y){
var bbox=canvas.getBoundingClientRect()//getBoundingClientRect 返回元素的6个值 left top right bottom width height
return {
x:x-bbox.left*(canvas.width/bbox.width),
y:y-bbox.top*(canvas.height/bbox.height)
}
}
function saveDrawingSurface(){
drawingSurfaceImageData=context.getImageData(0,0,canvas.width,canvas.height)
//getImageData() 方法返回 ImageData 对象,该对象拷贝了画布指定矩形的像素数据。
}
function restoreDrawingSurface(){
context.putImageData(drawingSurfaceImageData,0,0)
//putImageData() 方法将图像数据(从指定的 ImageData 对象)放回画布上。
}
function updateRubberbandRectangle(loc){
rubberbandRect.width=Math.abs(loc.x-mousedown.x) //用绝对值求线段的长度
rubberbandRect.height=Math.abs(loc.y-mousedown.y) //用绝对值求线段的高度
if(loc.x>mousedown.x){rubberbandRect.left=mousedown.x}else{
rubberbandRect.left=loc.x
}
if(loc.y>mousedown.y){rubberbandRect.top=mousedown.y}else{
rubberbandRect.top=loc.y
}
}
function drawRubberbandShape(loc){
context.beginPath()
context.moveTo(mousedown.x,mousedown.y)
context.lineTo(loc.x,loc.y)
context.stroke()
}
function updataRubberband(loc){ //loc是鼠标点在canvas上的坐标集合对象
// updateRubberbandRectangle(loc)
drawRubberbandShape(loc)
}
//这三个函数式辅助线函数
function drawHorizontalLine(y){
context.beginPath()
context.moveTo(0,y+0.5)
context.lineTo(context.canvas.width,y+0.5)
context.stroke()
}
function drawVerticalLine(x){
context.beginPath()
context.moveTo(x+0.5,0)
context.lineTo(x+0.5,context.canvas.height)
context.stroke()
}
function drawGuidewires(x,y){
context.save()
context.strokeStyle="rgba(0,0,230,0.4)"
context.lineWidth=0.5;
drawVerticalLine(x)
drawHorizontalLine(y)
context.restore()
}
//这三个函数式辅助线函数
canvas.onmousedown=function(e){ //只执行一次
var loc=windowToCanvas(e.clientX,e.clientY); //获取鼠标点在canvas的点坐标
e.preventDefault();
saveDrawingSurface() //复制canvas画布的像素
mousedown.x=loc.x; //鼠标点击的x轴坐标 这里mousedown记录的是初始位置
mousedown.y=loc.y; //鼠标点击的y轴坐标 这里mousedown记录的是初始位置
dragging=true;
}
canvas.onmousemove=function(e){
var loc;
if(dragging){
e.preventDefault()
loc=windowToCanvas(e.clientX,e.clientY);
restoreDrawingSurface()
updataRubberband(loc) //loc是鼠标点在canvas上的坐标集合对象
if(guidewires){ //辅助线
drawGuidewires(loc.x,loc.y)
}
}
}
canvas.onmouseup=function(e){
loc=windowToCanvas(e.clientX,e.clientY)
restoreDrawingSurface()
updataRubberband(loc)
dragging=false
}
strokeStyleSelect.onchange=function(){
context.strokeStyle=strokeStyleSelect.value
}
三次方贝塞尔曲线的绘制
bezierCurveTo
创建一条代表三次方贝塞尔曲线的路径。你需要向该方法传入三个点的坐标
,前两个点是该曲线的控制点,最后一个是锚点

canvas画多边形
由于canvas只提供了画矩形和圆形的方法,
所以涉及到多边形的话,只能自己写方法
其中涉及到 三角函数的API 比如
Math.PI圆周率
Math.sin
Math.cos
这俩就不多介绍了 sin cos 中学学的
大致思路就是只要有中心点 半径 就可以想画几边形就画几边形,
废话不多说,上代码
var canvas=document.getElementById("canvas");
var context=canvas.getContext("2d");
var Point = function (x, y) {
this.x = x;
this.y = y;
};
var Polygon=function(centerX,centerY,radius,sides,startAngle,strokeStyle,fillStyle,filled){
this.x=centerX;
this.y=centerY;
this.radius=radius;
this.sides=sides;
this.startAngle=startAngle;
this.strokeStyle=strokeStyle;
this.fillStyle=fillStyle;
this.filled=filled;
}
Polygon.prototype={
getPoints:function(){
var points=[];
var angle=this.startAngle||0;
for(var i=0;i<this.sides;i++){
points.push(new Point(this.x+this.radius*Math.cos(angle),this.y-this.radius*Math.sin(angle)))
angle+=2*Math.PI/this.sides
}
return points
},
createPath:function(){
var points=this.getPoints()
context.beginPath()
context.moveTo(points[0].x,points[0].y);
for(var i=1;i<points.length;i++){
context.lineTo(points[i].x,points[i].y)
}
context.closePath()
},
stroke:function(){
context.save()
this.createPath()
context.strokeStyle=this.strokeStyle;
context.stroke();
context.restore();
},
fill:function(){
context.save();
context.fillStyle=this.fillStyle;
this.createPath()
context.fill()
context.restore()
},
move: function (x, y) {
this.x = x;
this.y = y;
}
}
var test1=new Polygon(50,50,30,8,0,"red","red")
test1.stroke()
var test2=new Polygon(150,150,30,4,0,"red","red")
test2.fill()
isPointInPath
isPointInPath表示某个点在当前路径中就会返回true 反之返回false
var edite=false
var mousedown={}//记录鼠标按下起始点
var imageData1=null;
var mouseIn=null;//记录鼠标是否按了下去
var line_list={} //记录鼠标的终点
var Graphical=[];//存放图形引用的数组
var edit_list={}//记录在编辑模式下按的点
var map_name=null//每个生成图形的名称
var foucsIndex=null //被选中移动图形的下标
function saveImage(){ //储存此刻画布数据
imageData1= context.getImageData(0,0,canvas.width,canvas.height)
}
function loadingImage(){ //导入画布数据
context.putImageData(imageData1,0,0)
}
editCheckbox.onchange=function(){
if(this.checked){
console.log("进入编辑模式")
edite=true
}else{
console.log("进入画图模式")
edite=false
}
}
function windowToCanvas(event){ //算出你点击在canvas画布的坐标 并返回
var rect=canvas.getBoundingClientRect()
return {
x:event.clientX-rect.left,
y:event.clientY-rect.top
}
}
function line_width(){
var x_width=Math.abs(line_list.x-mousedown.x)
var y_width=Math.abs(line_list.y-mousedown.y)
return Math.sqrt(x_width*x_width+y_width*y_width)
}
canvas.onmousedown=function(event){
saveImage()
var some=windowToCanvas(event)
mousedown.x=some.x;
mousedown.y=some.y
mouseIn=true
Graphical.forEach(function(el,index){
el.createPath(context)
if(context.isPointInPath(mousedown.x,mousedown.y)){
edit_list.x=some.x;
edit_list.y=some.y
foucsIndex=index
}
})
}
canvas.onmousemove=function(event){
if(mouseIn&&edite){//如果按下鼠标并拖行 如果是编辑模式
loadingImage()
var offset_list=windowToCanvas(event);
var offsetX=offset_list.x-edit_list.x;
var offsetY=offset_list.y-edit_list.y;
context.clearRect(0,0,canvas.width,canvas.height)
Graphical[foucsIndex].move(mousedown.x+offsetX,mousedown.y+offsetY)
Graphical.forEach(function(el){
el.createPath(context)
el.fill(context)
})
}
if(mouseIn&&!edite){//如果按下鼠标并拖行 如果不是编辑模式
loadingImage()
line_list=windowToCanvas(event)
context.beginPath()
var r=line_width()
map_name=new Polygon(mousedown.x,mousedown.y,r,sidesSelect.value,0,"red","red")
map_name.fill(context)
}
}
canvas.onmouseup=function(){
mouseIn=false //鼠标抬起来了
if(!edite){
Graphical.push(map_name)
}
}
globalCompositeOperation
利用修改globalCompositeOperation改变canvas图像默认合成行为
<select name="" id="compositingSelect" size="11">
<option value="source-atop">source-atop</option>
<option value="source-in">source-in</option>
<option value="source-out">source-out</option>
<option value="source-over">source-over</option>
<option value="destination-atop">destination-atop</option>
<option value="destination-in">destination-in</option>
<option value="destination-out">destination-out</option>
<option value="destination-over">destination-over</option>
<option value="lighter">lighter</option>
<option value="copy">copy</option>
<option value="xor">xor</option>
</select>
11个合成行为
clip()剪切区域
这里有个坑要十分注意 调用clip()方法的时候,所定义的剪辑区域总是局限于期初的那个剪辑区域范围。
简单来说 clip()方法总是在上一次的剪辑区域基础上进行操作,所以说我们要把clip()方法放在save()和restore()方法中
clip 是对已有路径的剪切
var canvas=document.getElementById("canvas")
var context=canvas.getContext("2d")
function windowToCanvas(x,y){
var bbox=canvas.getBoundingClientRect()
return {
x:x-bbox.left,
y:y-bbox.top
}
}
function drawText(){
context.save()
context.shadowColor="rgba(100,100,150,0.8)"
context.shadowOffsetX=5;
context.shadowOffsetY=5;
context.shadowBlur=10;
context.fillStyle="cornflowerblue"
context.fillText("HTML5",20,250)
context.strokeStyle="yellow"
context.strokeText("HTML5",20,250)
context.restore()
}
function setClippingRegion(radius){
context.beginPath()
context.arc(canvas.width/2,canvas.height/2,radius,0,Math.PI*2,false)
context.clip()
}
function fillCanvas(color){
context.fillStyle=color
context.fillRect(0,0,canvas.width,canvas.height)
}
function endAnimation(loop){
clearInterval(loop)
setTimeout(function(e){
context.clearRect(0,0,canvas.width,canvas.height)
drawText()
},1000)
}
function drawAnimationFrame(radius){
setClippingRegion(radius);
fillCanvas('lightgray')
drawText()
}
function animate(){
var radius=canvas.width/2,
loop;
loop=window.setInterval(function(){
radius-=canvas.width/100
fillCanvas('charcoal')
if(radius>0){
context.save()
drawAnimationFrame(radius)
context.restore()
}else{
endAnimation(loop)
}
},16)
}
canvas.onmousedown=function(){
animate()
}
context.lineWidth=0.5
context.font='128pt Comic-sans'
drawText()
canvas制造渐变和图案填充文字.
这里主要是利用了 context的createPattern的方法
这个方法接受两个参数 第一个参数是要填充的图案,第二个参数是图案重复方式
制造颜色渐变的方法是createLinearGradient
var canvas=document.getElementById("canvas")
var context=canvas.getContext("2d")
var image=new Image();
var gradient=context.createLinearGradient(0,0,canvas.width,canvas.height)
var text="Canvas"
var pattern;
function drawGradientText(){
context.fillStyle=gradient
context.fillText(text,65,200)
context.strokeText(text,65,200)
}
function drawPatternText(){
context.fillStyle=pattern
context.fillText(text,65,450)
context.strokeText(text,65,450)
}
image.onload=function(e){
pattern=context.createPattern(image,"repeat")
drawPatternText()
}
image.src="./images/hover2.png"
context.font="256px Palation"
context.strokeStyle="cornflowerblue"
context.shadowColor="rgba(100,100,150,0.8)"
context.shadowOffsetX=5
context.shadowOffsetY=5
context.shadowBlur=10
gradient.addColorStop(0,"blue")
gradient.addColorStop(0.25,"blue")
gradient.addColorStop(0.5,"white")
gradient.addColorStop(0.75,'red')
gradient.addColorStop(1.0,'yellow')
drawGradientText()
canvas在圆弧周围绘制文本的两种写法
var canvas=document.getElementById("canvas")
var context=canvas.getContext("2d")
var TEXT_FILL_STYLE="rgba(100,130,240,0.5)"
var TEXT_STROKE_STYLE="rgba(200,0,0,0.7)"
var TEXT_SIZE=64;
circle={
x:canvas.width/2,
y:canvas.height/2,
radius:200
}
function drawCircularText(string,startAngle,endAngle){
var radius=circle.radius //圆的半径
var angleDecrement=(startAngle-endAngle)/(string.length-1)//每个字母占的弧度
var angle=parseFloat(startAngle) //转一下数字
var index=0;
var character;
context.save()
context.fillStyle=TEXT_FILL_STYLE;
context.strokeStyle=TEXT_STROKE_STYLE;
context.font=TEXT_SIZE+"px Lucida Sans"
while(index<string.length){
character=string.charAt(index)
context.save()
context.beginPath()
context.translate(circle.x+Math.cos(angle)*radius,circle.y-Math.sin(angle)*radius)
context.rotate(Math.PI/2-angle) //Math.PI/2为旋转90度 Math.PI/180*X为旋转多少度
context.fillText(character,0,0)
context.strokeText(character,0,0)
angle-=angleDecrement
index++
context.restore()
}
context.restore()
}
context.textAlign="center"
context.textBaseLine="middle"
drawCircularText("clockwise around the circle",Math.PI*2,Math.PI/8)
canvas实现图像的缩放
这里需要注意的是如果你向canvas之中绘制的图像有一部分落在canvas之外,
那么浏览器就会将canvas范围外的那部分图像忽略掉
var canvas=document.getElementById("canvas")
var context=canvas.getContext("2d")
var image=new Image()
var scaleSlider=document.getElementById("scaleSlider")
var scale=1.0
var MINIMUM_SCALE=1.0
var MAXIMUN_SCALE=3.0
function drawImage(){
var w=canvas.width
var h=canvas.height
var sw=w*scale
var sh=h*scale
context.clearRect(0,0,canvas.width,canvas.height)
context.drawImage(image,-sw/2+w/2,-sh/2+h/2,sw,sh)
}
function drawScaleText(value){
var text=parseFloat(value).toFixed(2)
scaleOutput.innerText=text
}
scaleSlider.onchange=function(e){
scale=e.target.value
if(scale<MINIMUM_SCALE){scale=MINIMUM_SCALE}
else if(scale>MAXIMUN_SCALE){scale=MAXIMUN_SCALE}
drawScaleText(scale)
drawImage()
}
image.src="./timg.jpg"
image.onload=function(e){
drawImage()
drawScaleText(scaleSlider.value)
}
利用离屏canvas提高复制图像放大的效率并添加水印
<!DOCTYPE html>
<html>
<head>
<title>图像与视频</title>
<meta charset="utf-8">
<style type="text/css">
*{padding: 0;margin: 0}
a{text-decoration: none}
img{border: none}
ul,ol{list-style: none}
br{font-size: 0;line-height: 0;clear: both}
body{text-align: center;background: rgba(100,145,250,0.3)}
canvas{border: 1px solid blue}
#scaleSlider{
vertical-align: 10px;
width: 100px;
margin-left: 90px;
}
#canvas{
margin:10px 20px 0px 20px;
border: thin solid #aaaaaa;
cursor:crosshair;
}
#controls{
margin-left: 15px;
padding: 0
}
#scaleOutput{
position: absolute;
width: 60px;
height: 30px;
margin-left: 10px;
vertical-align: center;
text-align:center;
color: blue;
font:18px Arial;
text-shadow: 2px 2px 4px rgba(100,140,250,0.8)
}
</style>
</head>
<body>
<div id="controls">
<output id="scaleOutput">1.0</output>
<input type="range" name="" id="scaleSlider" min="1" max="3.0" step="0.01" value="1.0">
</div>
<canvas id="canvas" id="canvas" width="454" height="512"></canvas>
</body>
</html>
<script type="text/javascript">
var canvas=document.getElementById("canvas")
var context=canvas.getContext("2d")
var offscreenCanvas=document.createElement("canvas") //创建离屏canvas
var offscreenContext=offscreenCanvas.getContext("2d")
offscreenCanvas.width=canvas.width
offscreenCanvas.height=canvas.height
var image=new Image()
var scaleSlider=document.getElementById("scaleSlider")
var scaleOutput=document.getElementById("scaleOutput")
//var scale=scaleSlider.value
var scale=1.0
var MINIMUM_SCALE=1.0
var MAXIMUN_SCALE=3.0
function drawScaled(){ //从离屏canvas复制过来图像并放大
var w=canvas.width;
var h=canvas.height;
var sw=w*scale;
var sh=h*scale;
context.drawImage(offscreenCanvas,0,0,offscreenCanvas.width,offscreenCanvas.height,-sw/2+w/2,-sh/2+h/2,sw,sh)
}
function drawScaleText(value){ //显示放大倍率
var text=parseFloat(value).toFixed(2)
scaleOutput.innerText=text
}
function drawWatermark(context){ //添加水印
var lineOne='Copyright'
var lineTwo='Acme Inc'
var textMetrics
var FONT_HEIGHT=128
context.save()
context.font=FONT_HEIGHT+"px Arial"
textMetrics=context.measureText(lineOne)
context.globalAlpha=0.6
context.fillStyle="cornflowerblue"
context.strokeStyle="yellow"
context.shadowColor='rgba(50,50,50,1.0)'
context.shaowOffsetX=5
context.translate(canvas.width/2,canvas.height/2-FONT_HEIGHT/2)
context.fillText(lineOne,-textMetrics.width/2,0)
context.strokeText(lineOne,-textMetrics.width/2,0)
textMetrics=context.measureText(lineTwo)
context.fillText(lineTwo,-textMetrics.width/2,FONT_HEIGHT)
context.strokeText(lineTwo,-textMetrics.width/2,FONT_HEIGHT)
context.restore()
}
scaleSlider.onchange=function(e)
scale=e.target.value
if (scale<MINIMUM_SCALE){scale=MINIMUM_SCALE}
else if(scale>MAXIMUN_SCALE){scale=MAXIMUN_SCALE}
drawScaled()
drawScaleText(scale)
}
image.src="./timg.jpg"
image.onload=function(){
context.drawImage(image,0,0,canvas.width,canvas.height)
offscreenContext.drawImage(canvas,0,0,canvas.width,canvas.height)
drawWatermark(context)
drawWatermark(offscreenContext)
drawScaleText(scale)
}
</script>
利用离屏canvas 手动放大图片任意局部位置效果
这里最重要的就是rubberbandEnd这个函数 它会判断鼠标从按下到松开 有没有位移过 如果没有就return 最后把离屏的图片选取局部复制到可见的canvas之中
<!DOCTYPE html>
<html>
<head>
<title>图像与视频</title>
<meta charset="utf-8">
<style type="text/css">
*{padding: 0;margin: 0}
a{text-decoration: none}
img{border: none}
ul,ol{list-style: none}
br{font-size: 0;line-height: 0;clear: both}
body{text-align: center;background: rgba(100,145,250,0.3)}
canvas{border: 1px solid blue}
#scaleSlider{
vertical-align: 10px;
width: 100px;
margin-left: 90px;
}
#canvas{
margin:10px 20px 0px 20px;
border: thin solid #aaaaaa;
cursor:crosshair;
}
#controls{
margin-left: 15px;
padding: 0
}
#scaleOutput{
position: absolute;
width: 60px;
height: 30px;
margin-left: 10px;
vertical-align: center;
text-align:center;
color: blue;
font:18px Arial;
text-shadow: 2px 2px 4px rgba(100,140,250,0.8)
}
</style>
</head>
<body>
<div id='controls'>
<input type='button' id='resetButton' value='Reset'/>
</div>
<canvas id="canvas" id="canvas" width="454" height="512"></canvas>
</body>
</html>
<script type="text/javascript">
console.log("start")
var canvas=document.getElementById("canvas")
var context=canvas.getContext("2d")
var offscreenCanvas=document.createElement("canvas") //创建离屏canvas
var offscreenContext=offscreenCanvas.getContext("2d")
offscreenCanvas.width=canvas.width
offscreenCanvas.height=canvas.height
var mousedown={}
var rubberbandRectangle={}
var dragging=false;
var start_data;
context.fillStyle='rgba(155,187,89,0.3)'
context.lineWidth=2
function windowToCanvas(canvas,x,y){
var bbox=canvas.getBoundingClientRect()
return {
x:x-bbox.left,
y:y-bbox.top
}
}
function rubberbandStretch(x,y){
rubberbandRectangle.left=Math.min(x,mousedown.x)
rubberbandRectangle.top=Math.min(y,mousedown.y)
rubberbandRectangle.width=Math.abs(x-mousedown.x)
rubberbandRectangle.height=Math.abs(y-mousedown.y)
context.fillRect(rubberbandRectangle.left+context.lineWidth,
rubberbandRectangle.top+context.lineWidth,
rubberbandRectangle.width-2*context.lineWidth,
rubberbandRectangle.height-2*context.lineWidth)
}
function rubberbandEnd(x,y){
if(x==rubberbandRectangle.left||y==rubberbandRectangle.top){
dragging=false;
return
}
context.clearRect(0,0,canvas.width,canvas.height)
context.drawImage(offscreenCanvas,rubberbandRectangle.left+context.lineWidth*2,
rubberbandRectangle.top+context.lineWidth*2,
rubberbandRectangle.width-4*context.lineWidth,
rubberbandRectangle.height-4*context.lineWidth,
0,0,canvas.width,canvas.height)
offscreenContext.drawImage(canvas,0,0,canvas.width,canvas.height)
dragging=false;
}
canvas.onmousedown=function(e){
var loc=windowToCanvas(canvas,e.clientX,e.clientY) //获取鼠标点击在canvas的坐标
e.preventDefault()
mousedown.x=loc.x
mousedown.y=loc.y;
rubberbandRectangle.left=mousedown.x
rubberbandRectangle.top=mousedown.y
dragging=true
}
canvas.onmousemove=function(e){
var loc
if(dragging){
context.clearRect(0,0,canvas.width,canvas.height)
context.drawImage(offscreenCanvas,0,0,canvas.width,canvas.height) //把离屏canvas上的数据复制到当前canvas
loc=windowToCanvas(canvas,e.clientX,e.clientY)
rubberbandStretch(loc.x,loc.y) //计算出选中的矩形坐标
}
}
canvas.onmouseup=function(e){
var loc=windowToCanvas(canvas,e.clientY,e.clientY)
rubberbandEnd(loc.x,loc.y) //鼠标抬起 开始从离屏canvas复制数据
}
resetButton.onclick=function(e){
dragging=false;
context.clearRect(0,0,canvas.width,canvas.height)
context.putImageData(start_data,0,0)
offscreenContext.putImageData(start_data,0,0)
}
function start(){
var src="./timg.jpg"
var img=new Image()
img.src=src
window.onload=function(){
context.drawImage(img,0,0,canvas.width,canvas.height)
start_data=context.getImageData(0,0,canvas.width,canvas.height) //记录初始数据以备还原使用
offscreenContext.drawImage(img,0,0,offscreenCanvas.width,offscreenCanvas.height) //离屏canvas储存图像
}
}
start()
</script>
canvas利用requestNextAnimationFrame做动画及碰撞检测
<!DOCTYPE html>
<head>
<title>Video</title>
<style>
body {
background: #dddddd;
}
#canvas {
background: #ffffff;
cursor: pointer;
margin-left: 10px;
margin-top: 10px;
-webkit-box-shadow: 3px 3px 6px rgba(0,0,0,0.5);
-moz-box-shadow: 3px 3px 6px rgba(0,0,0,0.5);
box-shadow: 3px 3px 6px rgba(0,0,0,0.5);
}
#controls {
margin-top: 10px;
margin-left: 15px;
}
</style>
</head>
<body>
<div id='controls'>
<input id='animateButton' type='button' value='Animate'/>
</div>
<canvas id='canvas' width='750' height='500'>
Canvas not supported
</canvas>
</body>
</html>
<script type="text/javascript" src="./js/requestNextAnimationFrame.js"></script>
<script type="text/javascript">
var canvas=document.getElementById("canvas")
var context=canvas.getContext("2d")
var paused=true
var discs=[
{x:150,
y:250,
lastX:150,
lastY:250,
velocityX:-3.2, //横坐标速率
velocityY:3.5, //纵坐标速率
radius:25,
innerColor:"rgba(255,255,0,1)",
middleColor:"rgba(255,255,0,0.7)",
outerColor:"rgba(255,255,0,0.5)",
strokeStyle:"gray"
},
{
x: 50,
y: 150,
lastX: 50,
lastY: 150,
velocityX: 2.2, //横坐标速率
velocityY: 2.5, //纵坐标速率
radius: 25,
innerColor: 'rgba(100,145,230,1.0)',
middleColor: 'rgba(100,145,230,0.7)',
outerColor: 'rgba(100,145,230,0.5)',
strokeStyle: 'blue'
},
{
x: 150,
y: 75,
lastX: 150,
lastY: 75,
velocityX: 1.2,
velocityY: 1.5,
radius: 25,
innerColor: 'rgba(255,0,0,1.0)',
middleColor: 'rgba(255,0,0,0.7)',
outerColor: 'rgba(255,0,0,0.5)',
strokeStyle: 'orange'
}
]
var numDiscs=discs.length; //圆的个数
var animateButton=document.getElementById("animateButton")
function update(){
var disc=null
for(var i=0;i<discs.length;i++){
disc=discs[i]
if(disc.x+disc.velocityX+disc.radius>canvas.width||disc.x-disc.radius+disc.velocityX<0){ //碰撞检测
disc.velocityX=-disc.velocityX
}
if(disc.y+disc.velocityY+disc.radius>canvas.height||disc.y-disc.radius+disc.velocityY<0){ //碰撞检测
disc.velocityY=-disc.velocityY
}
disc.x+=disc.velocityX
disc.y+=disc.velocityY
}
}
function draw(){
var disc=null
for(var i=0;i<discs.length;i++){
disc=discs[i]
gradient=context.createRadialGradient(disc.x,disc.y,0,disc.x,disc.y,disc.radius)
gradient.addColorStop(0.3,disc.innerColor)
gradient.addColorStop(0.5,disc.middleColor)
gradient.addColorStop(1.0,disc.outerColor)
context.save()
context.beginPath()
context.arc(disc.x,disc.y,disc.radius,0,Math.PI*2,false)
context.fillStyle=gradient
context.strokeStyle=disc.strokeStyle
context.fill()
context.stroke()
context.restore()
}
}
function animation(time){
if(!paused){
context.clearRect(0,0,canvas.width,canvas.height)
draw()
update()
window.requestNextAnimationFrame(animation)
}
}
animateButton.onclick=function(e){
paused=!paused
if(paused){
animateButton.value="Animation"
}else{
window.requestNextAnimationFrame(animation)
animateButton.value="Pause"
}
}
</script>
canvas制作无限视差滚动背景图
<!DOCTYPE html>
<html>
<head>
<title>背景滚动</title>
<meta charset="utf-8">
<style>
body {
background: #dddddd;
}
#canvas {
position: absolute;
top: 30px;
left: 10px;
background: #ffffff;
cursor: crosshair;
margin-left: 10px;
margin-top: 10px;
-webkit-box-shadow: 4px 4px 8px rgba(0,0,0,0.5);
-moz-box-shadow: 4px 4px 8px rgba(0,0,0,0.5);
box-shadow: 4px 4px 8px rgba(0,0,0,0.5);
}
input {
margin-left: 15px;
}
</style>
</head>
<body>
<canvas id='canvas' width='1024' height='512'>
Canvas not supported
</canvas>
<input id='animateButton' type='button' value='Animate'/>
<script src='./js/requestNextAnimationFrame.js'></script>
</body>
</html>
<script type="text/javascript">
var canvas=document.getElementById("canvas")
var context=canvas.getContext("2d")
var animateButton=document.getElementById("animateButton")
var sky=new Image()
var person=new Image()
var paused=true
var lastTime=0
var fps=0
var skyOffset=0
var skyOffset1=500
var SKY_VELOCITY=30
function erase(){
context.clearRect(0,0,canvas.width,canvas.height)
}
function getfps(){
var now=(+new Date)
fps=1000/(now-lastTime)
lastTime=now
return fps
}
function draw(){
context.save()
skyOffset=skyOffset>canvas.width?0:skyOffset+SKY_VELOCITY/getfps()
context.translate(skyOffset,0)
context.drawImage(sky,0,0,canvas.width,canvas.height)
context.drawImage(sky,-canvas.width+2,0,canvas.width,canvas.height)
context.restore()
context.save()
skyOffset1=skyOffset1-3
if(skyOffset1<-144){skyOffset1=canvas.width-144}
context.translate(0,0)
context.drawImage(person,skyOffset1,50)
context.drawImage(person,canvas.width+skyOffset1,50)
context.restore()
}
sky.src="./images/hover2.png"
person.src="./images/hover1.png"
window.onload=function(){
draw()
}
function animate(){
if(paused){
erase()
draw()
}
requestNextAnimationFrame(animate)
}
window.requestNextAnimationFrame(animate)
animateButton.onclick=function(){
paused=!paused
}
</script>
canvas制作随机粒子移动特效
<!DOCTYPE html>
<html>
<head>
<title>背景滚动</title>
<meta charset="utf-8">
<style>
body {
background: #dddddd;
}
#canvas {
position: absolute;
top: 30px;
left: 10px;
background: #ffffff;
cursor: crosshair;
margin-left: 10px;
margin-top: 10px;
-webkit-box-shadow: 4px 4px 8px rgba(0,0,0,0.5);
-moz-box-shadow: 4px 4px 8px rgba(0,0,0,0.5);
box-shadow: 4px 4px 8px rgba(0,0,0,0.5);
}
input {
margin-left: 15px;
}
</style>
</head>
<body>
<canvas id='canvas' width='1024' height='512'>
Canvas not supported
</canvas>
<input id='animateButton' type='button' value='Animate'/>
<script src='./js/requestNextAnimationFrame.js'></script>
</body>
</html>
<script type="text/javascript">
var canvas=document.getElementById("canvas")
var context=canvas.getContext("2d")
var Sprite=function(name,painter,behaviors){
if(name!==undefined){this.name=name}
if(painter!==undefined){this.painter=painter}
this.top=0
this.left=0
this.width=0
this.height=0
this.velocityX=0
this.velocityY=0
this.visible=true
this.animating=false
this.behaviors=behaviors||[]
this.RADIUS=0
return this
}
Sprite.prototype={
paint:function(context){
if(this.painter!==undefined&&this.visible){this.painter.paint(this,context)}
},
update:function(context,time){
for(var i=0;i<this.behaviors.length;i++){
this.behaviors[i].excute(this,context,time)
}
}
}
// var RADIUS=5
var pai={
paint:function(sprite,context){
context.save()
context.beginPath()
sprite.left=sprite.left+sprite.velocityX
sprite.top=sprite.top+sprite.velocityY
if(sprite.left+sprite.RADIUS>canvas.width||sprite.left-sprite.RADIUS<0){
sprite.velocityX=-sprite.velocityX
}
if(sprite.top+sprite.RADIUS>canvas.height||sprite.top-sprite.RADIUS<0){
sprite.velocityY=-sprite.velocityY
}
context.arc(sprite.left+sprite.width/2,sprite.top+sprite.height/2,sprite.RADIUS,0,Math.PI*2,false)
context.clip()
context.shadowBlur=8
context.lineWidth=2
context.strokeStyle="rgb(100,100,195)"
context.fillStyle="rgba(30,144,255,0.15)"
context.fill()
context.stroke()
context.restore()
}
}
var star_list=[]
for(var i=0;i<100;i++){
var star=new Sprite("star"+i,pai)
star.left=parseInt(19+(Math.random()*800))
star.top=parseInt(19+(Math.random()*450))
if(i%2==0){
star.velocityY=parseInt(Math.random()*15+1)
star.velocityX=parseInt(Math.random()*15+1)
}else{
star.velocityY=-(parseInt(Math.random()*15+1))
star.velocityX=-(parseInt(Math.random()*15+1))
}
star.RADIUS=parseInt(Math.random()*20+1)
star_list.push(star)
}
function animate(){
context.clearRect(0,0,canvas.width,canvas.height)
context.fillStyle="rgb(0,0,0)"
context.fillRect(0,0,canvas.width,canvas.height)
for(var i=0;i<star_list.length;i++){
star_list[i].paint(context)
}
window.requestAnimationFrame(animate)
}
window.requestAnimationFrame(animate)
</script>
canvas精灵图制作人物左右移动
var velocityX=10 //X轴的位移速度
var left1=100 //X轴的起始位置 这个值控制人物的横坐标
var img_start=imgid //最开始的精灵贴图
var advance_or="left" //最开始向左移动
SpriteSheetPainter=function(cells){
this.cells=cells||[]
this.cellIndex=0
}
SpriteSheetPainter.prototype={
advance:function(){
if(this.cellIndex==(this.cells.length-1)){this.cellIndex=0}else{
this.cellIndex++
}
},
advance2:function(){
if(this.cellIndex==0){this.cellIndex=(this.cells.length-1)}else{
this.cellIndex--
}
},
paint:function(context,canvas){
var cell=this.cells[this.cellIndex]
left1=canvas.left-velocityX
if(left1<0){ //人物左移临界点判断
advance_or="right"
velocityX=-velocityX
img_start=imgid2
context.drawImage(img_start,cell.x,cell.y,cell.w,cell.h,canvas.left,canvas.top,canvas.w,canvas.h)
}
if(left1+canvas.w>1024){ //人物右移临界点判断
advance_or="left"
velocityX=-velocityX
img_start=imgid
context.drawImage(img_start,cell.x,cell.y,cell.w,cell.h,canvas.left,canvas.top,canvas.w,canvas.h)
}
else{
if(advance_or=="left"){
img_start=imgid
this.advance()
}
if(advance_or=="right"){
img_start=imgid2
this.advance2()
}
context.drawImage(img_start,cell.x,cell.y,cell.w,cell.h,canvas.left,canvas.top,canvas.w,canvas.h)
}
}
}
window.onload=function(){
//bomb.paint(context)
var some=new SpriteSheetPainter([{x:0,y:0,w:41,h:64},
{x:53,y:0,w:41,h:64},
{x:106,y:0,w:41,h:64},
{x:159,y:0,w:41,h:64},
{x:212,y:0,w:41,h:64},
{x:265,y:0,w:41,h:64},
{x:318,y:0,w:41,h:64},
{x:371,y:0,w:41,h:64},
{x:424,y:0,w:41,h:64}
])
some.paint(context,{left:100,top:100,w:53,h:64})
var now=0
var last=0
function animate(){
now=(+new Date)
var hi=now-last
if(parseInt(hi)>100){
last=now
context.clearRect(0,0,canvas.width,canvas.height)
// some.advance()
some.paint(context,{left:left1,top:100,w:53,h:64})
}
requestAnimationFrame(animate)
}
requestAnimationFrame(animate)
}
网友评论