初识canvas

canvas元素

  • canvas标签
    如果不给 设置 widht、height 属性时,则默认 width为300、height 为 150,单位都是 px。
    也可以使用 css 属性来设置宽高,但是如宽高属性和初始比例不一致,他会出现扭曲。
    所以,建议永远不要使用 css 属性来设置 的宽高。
<canvas class="box" id="demo1" width="400" height="400"></canvas>
  • 替代内容
    支持 的浏览器会只渲染 标签,而忽略其中的替代内容。不支持 的浏览器则 会直接渲染替代内容。
  • 结束标签
    如果结束标签不存在,则文档的其余部分会被认为是替代内容,将不会显示出来。
  <!-- <canvas class="box" id="demo1" width="400" height="400"> 你的浏览器不支持 canvas,请升级你的浏览器。</canvas> -->
    <canvas class="box" id="demo1" width="400" height="400">
      <img src="./html.gif" alt="不支持canvas时替代的图片内容">
    </canvas>

渲染上下文+检测浏览器支持性

var canvas = document.getElementById('demo1')
if (canvas.getContext) {
  var ctx = canvas.getContext('2d')
  alert('支持canvas')
} else {
  alert('不支持canvas')
}

绘制矩形

/**
 * 1.绘制填充矩形:fillRect(x, y, width, height)
 * 2.绘制矩形边框:strokeRect(x, y, width, height)
 * 3.清除矩形指定区域,使得该区域完全透明:clearRect(x, y, widh, height)
 */
function draw() {
  var canvas = document.getElementById('demo1')
  if (!canvas.getContext) return
  var ctx = canvas.getContext('2d')
  
  // 不做颜色填充,默认为black
  ctx.fillRect(540, 0, 100, 100)
  ctx.strokeRect(660, 0, 200, 100)
  
  // 绘制矩形填充
  ctx.fillStyle = 'rgb(255,0,0)'
  ctx.fillRect(0, 0, 200, 100)
  // 清除矩形指定区域
  ctx.clearRect(50, 50, 30, 30)

  // 绘制矩形边框
  ctx.strokeStyle = 'rgb(0,255,0)'
  ctx.strokeRect(220, 0, 300, 100)
}
draw()

在这里插入图片描述

绘制路径

/**
* 1.beginPath() 新建一条路径,路径一旦创建成功,图形绘制命令被指向到路径上生成路径
* 2.moveTo(x,y) 把画笔移动到指定的坐标(x, y)。相当于设置路径的起始点坐标
* 3.closePath() 闭合路径之后,图形绘制命令又重新指向到上下文中
* 4.stroke() 通过线条来绘制图形轮廓
* 5.fill() 通过填充路径的内容区域生成实心的图形
* 
* 线段:
* lineTo(x,y)
* 圆弧:
* arc(x, y, r, startAngle, endAngle, anticlockwise): 以(x, y) 为圆心,以r 为半径,从 startAngle 弧度开始到endAngle弧度结束。anticlosewise 是布尔值,true 表示逆时针,false 表示顺时针(默认是顺时针)
* arcTo(x1, y1, x2, y2, radius): 根据给定的控制点和半径画一段圆弧,最后再以直线连接两个控制点
* 贝塞尔曲线:
* 一次贝塞尔:一条直线
* 二次贝塞尔:quadraticCurveTo(cp1x, cp1y, x, y) //控制点坐标,结束点坐标
* 三次贝塞尔:bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) //控制1点坐标,控制2点坐标,结束点坐标
*/

function draw() {
var canvas = document.getElementById('demo1')
if (!canvas.getContext) return
var ctx = canvas.getContext('2d')

// 绘制线段
ctx.beginPath()
ctx.moveTo(10, 10)
ctx.lineTo(50, 50)
ctx.closePath()
ctx.stroke()

// 绘制三角形
ctx.beginPath()
ctx.moveTo(60, 10)
ctx.lineTo(100, 10)
ctx.lineTo(100, 50)
ctx.lineTo(60, 10)
ctx.closePath()
ctx.stroke()

// 填充三角形
ctx.beginPath()
ctx.moveTo(110, 10)
ctx.lineTo(110, 50)
ctx.lineTo(160, 50)
ctx.lineTo(110, 10)
ctx.closePath()
ctx.fillStyle = 'rgb(0,0,255)'
ctx.fill()

// 绘制圆弧arc
ctx.beginPath()
ctx.arc(200, 30, 30, 0, Math.PI * 2, true)
ctx.closePath()
ctx.strokeStyle = 'rgb(0,255,0)'
ctx.stroke()

ctx.beginPath()
ctx.arc(270, 30, 30, 0, -Math.PI, false)
ctx.closePath()
ctx.strokeStyle = 'rgb(255,0,0)'
ctx.stroke()

ctx.beginPath()
ctx.arc(340, 30, 30, 0, -Math.PI / 2, true)
ctx.closePath()
ctx.strokeStyle = 'rgb(0,200,0)'
ctx.stroke()

// 绘制圆弧arcTo()
ctx.beginPath();
ctx.moveTo(410, 0); // 起始点
ctx.arcTo(450, 0, 450, 40, 50); // 控制点1,控制点2,半径
ctx.lineTo(450, 40)
ctx.arcTo(450, 80, 490, 80, 50)
ctx.lineTo(490, 80)
ctx.strokeStyle = 'rgb(100,100,200)'
ctx.stroke();

// 绘制二次贝塞尔
ctx.beginPath()
ctx.moveTo(500, 50)
ctx.quadraticCurveTo(600, 10, 660, 70)
ctx.strokeStyle = 'rgb(200,200,100)'
ctx.stroke()

// 绘制三次贝塞尔
ctx.beginPath()
ctx.moveTo(670, 60)
ctx.bezierCurveTo(650, 20, 700, 30, 720, 80)
ctx.strokeStyle = 'rgb(125,125,100)'
ctx.stroke()
}
draw()

在这里插入图片描述

添加样式和颜色

/**
* 1.fillStyle=color 设置图形的填充颜色
* 2.strokeStyle=color 设置图形轮廓的颜色
* 3.globalAlpha = transparencyValue 透明度(0完全透明;1不透明)。 globalAlpha 属性在需要绘制大量拥有相同透明度的图形时候相当高效,不过,我认为使用rgba()设置透明度更加好一些
* 4.line style:ctx.lineWidth=20,设置线条宽度,默认为1.0【起点和终点的连线为中心,上下各占线宽的一半】
* 5.lineCap=type 线条末端样式(butt,round,square)
* 6.lineJoin=type 线条与线条间接合处的样式(round,level,miter)
* 7.setLineDash([实线长度,间隙长度]),lineDashOffset=起始偏移量
*/
function draw() {
  var canvas = document.getElementById('demo1')
  if (!canvas.getContext) return
  var ctx = canvas.getContext('2d')

  // fillStyle
  // for (var i = 0; i < 6; i++) {
  //   for (var j = 0; j < 6; j++) {
  //     ctx.fillStyle = `rgb(${Math.floor(255 - 42.5*i)},${Math.floor(255-42.5*j)},0)`
  //     ctx.fillRect(j * 50, i * 50, 50, 50);
  //   }
  // }

  // strokeStyle
  // for (var i = 6; i < 12; i++) {
  //   for (var j = 6; j < 12; j++) {
  //     ctx.strokeStyle = `rgb(${randomInt(0,255)},${randomInt(0,255)},${randomInt(0,255)})`
  //     ctx.strokeRect(j * 50, i * 50, 40, 40)
  //   }
  // }

  // line style:线宽,只能是正值,默认1.0
  ctx.beginPath()
  ctx.moveTo(0, 0)
  ctx.lineTo(50, 50)
  ctx.stroke()

  ctx.beginPath()
  ctx.moveTo(50, 50)
  ctx.lineTo(100, 0)
  ctx.lineWidth = 10
  ctx.stroke()

  ctx.beginPath()
  ctx.moveTo(100, 0)
  ctx.lineTo(150, 50)
  ctx.lineWidth = 20
  ctx.stroke()

  // lineCap = type:线条末端样式。butt线段末端以方形结束,round线段末端以圆形结束,square线段末端以方形结束
  ctx.beginPath()
  ctx.moveTo(180, 10)
  ctx.lineTo(180, 50)
  ctx.lineWidth = 10
  ctx.lineCap = 'butt'
  ctx.strokeStyle = 'rgb(255,0,0)'
  ctx.stroke()

  ctx.beginPath()
  ctx.moveTo(200, 10)
  ctx.lineTo(200, 50)
  ctx.lineWidth = 10
  ctx.lineCap = 'round'
  ctx.stroke()

  ctx.beginPath()
  ctx.moveTo(220, 10)
  ctx.lineTo(220, 50)
  ctx.lineWidth = 10
  ctx.lineCap = 'square'
  ctx.stroke()

  // lineJoin = type:同一个path内,设定线条与线条间结合处的样式。round 通过填充一个额外的,圆心在相连部分末端的扇形,绘制拐角的形状。 圆角的半径是线段的宽度,bevel 在相连部分的末端填充一个额外的以三角形为底的区域, 每个部分都有各自独立的矩形拐角,miter(默认) 通过延伸相连部分的外边缘,使其相交于一点,形成一个额外的菱形区域
  ctx.beginPath()
  ctx.moveTo(240, 10)
  ctx.lineTo(260, 50)
  ctx.lineTo(280, 10)
  ctx.lineTo(300, 50)
  ctx.lineTo(320, 10)
  ctx.lineWidth = 10
  ctx.strokeStyle = 'rgb(0,255,0)'
  ctx.lineJoin = 'round'

  ctx.beginPath()
  ctx.moveTo(240, 10)
  ctx.lineTo(260, 50)
  ctx.lineTo(280, 10)
  ctx.lineWidth = 10
  ctx.strokeStyle = 'rgb(0,255,0)'
  ctx.lineJoin = 'round'
  ctx.stroke()

  ctx.beginPath()
  ctx.moveTo(300, 10)
  ctx.lineTo(320, 50)
  ctx.lineTo(340, 10)
  ctx.lineWidth = 10
  ctx.lineJoin = 'bevel'
  ctx.stroke()

  ctx.beginPath()
  ctx.moveTo(360, 10)
  ctx.lineTo(380, 50)
  ctx.lineTo(400, 10)
  ctx.lineWidth = 10
  ctx.lineJoin = 'miter'
  ctx.stroke()

  // 虚线
  ctx.setLineDash([5, 3])
  ctx.lineDashOffset = -0
  ctx.strokeStyle = 'rgb(0,0,255)'
  ctx.lineWidth = 1
  ctx.strokeRect(420, 10, 50, 30)
}

// function randomInt(from, to) {
//   return parseInt(Math.random() * (to - from + 1) + from)
// }
draw()

在这里插入图片描述

绘制文本

 /**
* 1.fillText(text, x, y [, maxWidth]) 在指定的 (x,y) 位置填充指定的文本,绘制的最大宽度是可选的
* 2.strokeText(text, x, y [, maxWidth]) 在指定的 (x,y) 位置绘制文本边框,绘制的最大宽度是可选的
* 
* 给文本添加样式:
* font=value:与css font语法相同,默认是10px sans-serif
* textAlign=value:文本对齐选项,可选值包括(start,end,left,right,center,默认start)
* textBaseline=value:基线对齐选项,可选值包括(top,hanging,middle,alphabetic,ideographic,bottom,默认alphabetic)
* direction=value:文本方向,可选值包括(ltr,rtl,inherit,默认是inherit)
*/
  function draw() {
  var canvas = document.getElementById('demo1')
  if (!canvas.getContext) return
  var ctx = canvas.getContext('2d')

  ctx.font = "30px sans-serif"
  ctx.fillText('爱屋及乌', 0, 30)
  ctx.strokeText('比翼双飞', 0, 100)
}
draw()

在这里插入图片描述

绘制图片

 <img src="./pic2.jpeg" width="360" alt=""><br>
<canvas class="box" id="demo1" width="720" height="420">
   <img src="./html.gif" alt="不支持canvas时替代的图片内容">
</canvas>
 /**
* 绘图:ctx.drawImage(image,x,y)
* 缩放:ctx.drawImage(image,x,y,width,height)
* 切片:drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) 前 4 个是定义图像源的切片位置和大小,后 4 个则是定义切片的目标显示位置和大小
*/
function draw() {
  var canvas = document.getElementById('demo1')
  if (!canvas.getContext) return
  var ctx = canvas.getContext('2d')

  // 绘制图片
  // var img = new Image()
  // img.src = './pic1.jpeg'
  // // 考虑到图片是从网络加载,如果 drawImage 的时候图片还没有完全加载完成,则什么都不做,个别浏览器会抛异常。所以我们应该保证在 img 绘制完成之后再 drawImage。
  // img.onload = function () {
  //   // ctx.drawImage(img, 0, 0)
  //   // 图片缩放
  //   ctx.drawImage(img, 0, 0, 360, 210)
  // }

  // 绘制img标签元素中的图片
  // var img = document.querySelector('img')
  // ctx.drawImage(img, 0, 0)

  // 切片
  var img = new Image()
  img.src = './pic2.jpeg'
  img.onload = function () {
    ctx.drawImage(img, 10, 10, 60, 60, 0, 0, 100, 100)
  }
}
// document.querySelector('img').onclick = function () {
//   draw()
// }
draw()

保存状态和恢复

/**
 * save() 每次使用save的时候,当前的状态就被push进栈
 * restore() 每次使用restore的时候,上一个栈中的状态就pop出来
 */
function draw() {
  var canvas = document.getElementById('demo1')
  if (!canvas.getContext) return
  var ctx = canvas.getContext('2d')

  ctx.fillRect(0, 0, 200, 100)
  ctx.save()

  ctx.fillStyle = 'rgb(255,0,0)'
  ctx.fillRect(210, 0, 200, 100)
  ctx.save()

  ctx.restore()
  ctx.fillRect(420, 0, 200, 100)

  ctx.restore()
  ctx.fillRect(630, 0, 200, 100)
}
draw()

在这里插入图片描述

变形

 /**
 * 移动:translate(x, y) x左右偏移量,y上下偏移量
 * 旋转:rotate(angle) 顺时针,以弧度为单位
 * 缩放:scale(x, y) 大于1表示放大,小于1表示缩小
 * 变形矩阵:transform(a,b,c,d,e,f)
 */
function draw() {
  var canvas = document.getElementById('demo1')
  if (!canvas.getContext) return
  var ctx = canvas.getContext('2d')

  // ctx.fillRect(0, 0, 20, 20)
  // ctx.scale(2, 2)
  // ctx.fillRect(0, 30, 20, 20)
  // ctx.translate(50, 0)
  // ctx.fillRect(0, 0, 20, 20)
  // ctx.rotate(Math.PI / 6 * 2)
  // ctx.fillRect(20, 20, 20, 20)

  ctx.transform(1, 1, 0, 1, 0, 0)
  ctx.fillStyle = 'rgb(255,0,0)'
  ctx.fillRect(0, 0, 100, 100)
}
draw()

合成

/**
 * globalCompositeOperation=type:可选值包括('source-over','destination-over','source-in','destination-in','source-out','destination-out','source-atop','destination-atop','lighter','lighten','darken','xor','copy')
 */
function draw() {
  var canvas = document.getElementById('demo1')
  if (!canvas.getContext) return
  var ctx = canvas.getContext('2d')

  var types = ['source-over', 'destination-over', 'source-in', 'destination-in', 'source-out', 'destination-out',
    'source-atop', 'destination-atop', 'lighter', 'lighten', 'darken', 'xor', 'copy'
  ]
  var i = 0
  let interval = setInterval(function () {
    if (i == 13) {
      i = 0
    } else {
      i++
    }
    ctx.fillStyle = 'rgb(255,0,0)'
    ctx.fillRect(0, 0, 100, 100)
    ctx.globalCompositeOperation = types[i]
    ctx.fillStyle = 'rgb(0,255,0)'
    ctx.fillRect(50, 50, 100, 100)
  }, 1000)
}
draw()

裁剪

/**
 * clip()
 */
function draw() {
  var canvas = document.getElementById('demo1')
  if (!canvas.getContext) return
  var ctx = canvas.getContext('2d')

  var img = new Image()
  img.onload = function () {
    drawImage(ctx, img)
  }
  img.src = './pic1.jpeg'
}

function drawImage(ctx, img) {
  // 三角形区域裁剪
  // clipTriangle(ctx)
  // 矩形区域裁剪
  // clipRectangle(ctx)
  // 圆形区域裁剪
  // clipCircle(ctx)
  // 星形区域裁剪
  clipStar(ctx)
  ctx.drawImage(img, 0, 0)
}

function clipTriangle(ctx) {
  ctx.beginPath()
  ctx.moveTo(10, 10)
  ctx.lineTo(110, 10)
  ctx.lineTo(60, 200)
  ctx.lineTo(10, 10)
  ctx.closePath()
  ctx.clip()
}

function clipRectangle(ctx) {
  ctx.beginPath()
  ctx.moveTo(10, 10)
  ctx.lineTo(210, 10)
  ctx.lineTo(210, 110)
  ctx.lineTo(10, 110)
  ctx.lineTo(10, 10)
  ctx.closePath()
  ctx.clip()
}

function clipCircle(ctx) {
  ctx.beginPath()
  ctx.arc(200, 200, 100, 0, Math.PI * 2, true)
  ctx.closePath()
  ctx.clip()
}

function clipStar(ctx) {
  var n = 0
  var dx = 150
  var dy = 100
  var s = 100
  ctx.beginPath()
  var x = Math.sin(0)
  var y = Math.cos(0)
  var dig = (Math.PI / 5) * 4
  for (var i = 0; i < 5; i++) {
    var x = Math.sin(i * dig)
    var y = Math.cos(i * dig)
    ctx.lineTo(dx + x * s, dy + y * s)
  }
  ctx.closePath()
  ctx.clip()
}
draw()

动画

/**
 * 控制动画:
 * setInterval()
 * setTimeout()
 * requestAnimationFrame()
 */
function init() {
  let canvas = document.getElementById('demo1')
  if (!canvas.getContext) return
  let ctx = canvas.getContext('2d')
  draw(ctx)
}

function draw(ctx) {
  requestAnimationFrame(function step() {
    drawDial(ctx) //绘制表盘
    drawAllHands(ctx) //绘制时分秒针
    requestAnimationFrame(step)
  })
}

function drawAllHands(ctx) {
  // 获取当前时间和时、分、秒
  let time = new Date()
  let s = time.getSeconds()
  let m = time.getMinutes()
  let h = time.getHours()

  //计算出秒、分、时针的弧度
  let pi = Math.PI
  let secondAngle = pi / 180 * 6 * s
  let minuteAngle = pi / 180 * 6 * m + secondAngle / 60
  let hourAngle = pi / 180 * 30 * h + minuteAngle / 12

  // 绘制时分秒针(针角度、针长度、针宽度、针颜色、参数ctx)
  drawHand(hourAngle, 60, 6, "red", ctx)
  drawHand(minuteAngle, 106, 4, "green", ctx)
  drawHand(secondAngle, 129, 2, "blue", ctx)
}

function drawHand(angle, len, width, color, ctx) {
  ctx.save()
  ctx.translate(150, 150)
  ctx.rotate(-Math.PI / 2 + angle)
  ctx.beginPath()
  ctx.moveTo(-4, 0)
  ctx.lineTo(len, 0)
  ctx.lineWidth = width
  ctx.strokeStyle = color
  ctx.lineCap = 'round'
  ctx.stroke()
  ctx.closePath()
  ctx.restore()
}

// 绘制表盘
function drawDial(ctx) {
  let pi = Math.PI
  ctx.clearRect(0, 0, 300, 300)
  ctx.save()

  ctx.translate(150, 150)
  ctx.beginPath()
  ctx.arc(0, 0, 148, 0, 2 * pi)
  ctx.stroke()
  ctx.closePath()

  for (let i = 0; i < 60; i++) {
    ctx.save()
    ctx.rotate(-pi * 2 + i * pi / 30)
    ctx.beginPath()
    ctx.moveTo(110, 0)
    ctx.lineTo(140, 0)
    ctx.lineWidth = i % 5 ? 2 : 4
    ctx.strokeStyle = i % 5 ? 'blue' : 'red'
    ctx.stroke()
    ctx.closePath()
    ctx.restore()
  }
  ctx.restore()
}
init()

综合案例

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>canvas画图</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    .canvas-box {
      display: flex;
      flex-wrap: wrap;
      justify-content: space-around;
      align-items: center;
    }

    .demo {
      width: 30%;
    }
  </style>
</head>

<body>
  <button class="btn"
    onclick="drawRect();drawCircle();drawTest();drawMoveLine();drawMoveLine();drawFlower();drawBezier();drawGradient();drawGradient1();drawTrans();drawComposite();drawShadow();paintImage();drawRepeat();drawClip();drawHandlePx();drawText();drawSave()">
    绘制
  </button>
  <div class="canvas-box">
    <canvas class="demo" id="rect"></canvas>
    <canvas class="demo" id="circle"></canvas>
    <canvas class="demo" id="test"></canvas>
    <canvas class="demo" id="moveLine"></canvas>
    <canvas class="demo" id="flower"></canvas>
    <canvas class="demo" id="bezier"></canvas>
    <canvas class="demo" id="gradient"></canvas>
    <canvas class="demo" id="gradient1"></canvas>
    <canvas class="demo" id="trans"></canvas>
    <canvas class="demo" id="composite"></canvas>
    <canvas class="demo" id="shadow"></canvas>
    <canvas class="demo" id="image"></canvas>
    <canvas class="demo" id="repeat"></canvas>
    <canvas class="demo" id="clip"></canvas>
    <canvas class="demo" id="handlePx"></canvas>
    <canvas class="demo" id="text"></canvas>
    <canvas class="demo" id="save"></canvas>
  </div>

  <script>
    /**
     * 1.获取context对象
     *    var context =canvas.getContext("2d");
     * 2.绘制图像的两种方式
     *    context.fill()//填充
     *    context.stroke()//绘制边框
     * 3.设置绘制图像的样式
     *    context.fillStyle//填充的样式
     *    context.strokeStyle//边框样式
     *    context.lineWidth//图形边框宽度
     * 4.绘制矩形(x起点横坐标,y起点纵坐标,width矩形长度,height矩形高度)
     *    context.fillRect(x,y,width,height)
     *    strokeRect(x,y,width,height)
     * 5.清除矩形区域(x清除起点横坐标,y清除起点纵坐标,width清除长度,height清除高度)
     *    context.clearRect(x,y,width,height)
     * 6.绘制圆弧(x圆心x坐标,y圆心y坐标,starAngle起始角度,endAngle结束角度,anticlockwise是否逆时针)
     *    ps:经过试验证明书本上ture是顺时针,false是逆时针是错误的,而且无论是逆时针还是顺时针,角度都沿着顺时针扩大
     *    context.arc(x, y, radius, starAngle,endAngle, anticlockwise)
     * 7.context.beginPath()、context.closePath()
     * 8.绘制线段(每次划线从moveTo的点到lineTo的点;如果没有moveTo,那第一次lineTo和moveTo的效果一样;每次lineTo之后如果没有moveTo,则下一次lineTo的起点为此次lineTo的结束点)
     *    context.moveTo(x,y)
     *    context.lineTo(x,y)
     * 9.绘制贝塞尔曲线
     *    context.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y)  (cp1x第一个控制点x坐标,cp1y第一个控制点y坐标,cp2x第二个控制点x坐标,cp2y第二个控制点y坐标,x终点x坐标,y终点y坐标)
     *    绘制二次样条曲线 context.quadraticCurveTo(qcpx,qcpy,qx,qy)  (qcpx控制点x坐标,qcpy控制点y坐标,qx终点x坐标,qy终点y坐标)
     * 10.线性渐变
     *    var lg= context.createLinearGradient(xStart,yStart,xEnd,yEnd) (xStart渐变开始x坐标,yStart渐变开始y坐标,xEnd渐变结束x坐标,yEnd渐变结束y坐标)
     *    lg.addColorStop(offset,color) (offset设定的颜色离渐变结束的偏移量,color绘制时要使用的颜色)
     * 11.径向渐变
     *    var rg=context.createRadialGradient(xStart,yStart,radiusStart,xEnd,yEnd,radiusEnd)  (xStart发散开始圆心x坐标,yStart发散开始圆心y坐标,radiusStart发散开始圆半径,xEnd发散结束x坐标,yEnd发散结束y坐标,radiusEnd发散结束圆半径)
     *    rg.addColorStop(offset,color)  (offset:设定的颜色离渐变结束点的偏移量(0~1),color:绘制时要使用的颜色)
     * 12.图形变形
     *    平移:context.translate(x,y)  (向x轴平移x,向y轴平移y)
     *    缩放:context.scale(x,y)  (按x比例缩放,按y比例缩放)
     *    旋转:context.rotate(angle) (旋转angle度)
     * 13.矩阵变换
     *    context.transform(m11,m12,m21,m22,dx,dy)
     *    context.translate(x,y) 等同于context.transform (1,0,0,1,x,y)或context.transform(0,1,1,0.x,y)
     *    context.scale(x,y)等同于context.transform(x,0,0,y,0,0)或context.transform (0,y,x,0, 0,0)
     *    context.rotate(θ)等同于context.transform(Math.cos(θ*Math.PI/180),Math.sin(θ*Math.PI/180),-Math.sin(θ*Math.PI/180),Math.cos(θ*Math.PI/180),0,0)或 context.transform(-Math.sin(θ*Math.PI/180),Math.cos(θ*Math.PI/180),Math.cos(θ*Math.PI/180),Math.sin(θ*Math.PI/180), 0,0)
     * 14.图形组合
     *    context.globalCompositeOperation=type
     *    type类型:
     *        source-over(默认值):在原有图形上绘制新图形
     *        destination-over:在原有图形下绘制新图形
     *        source-in:显示原有图形和新图形的交集,新图形在上,所以颜色为新图形的颜色
     *        destination-in:显示原有图形和新图形的交集,原有图形在上,所以颜色为原有图形的颜色
     *        source-out:只显示新图形非交集部分
     *        destination-out:只显示原有图形非交集部分
     *        source-atop:显示原有图形和交集部分,新图形在上,所以交集部分的颜色为新图形的颜色
     *        destination-atop:显示新图形和交集部分,新图形在下,所以交集部分的颜色为原有图形的颜色
     *        lighter:原有图形和新图形都显示,交集部分做颜色叠加
     *        xor:重叠飞部分不现实
     *        copy:只显示新图形
     * 15.绘制阴影
     *    context.shadowOffsetX :阴影的横向位移量(默认值为0)
     *    context.shadowOffsetY :阴影的纵向位移量(默认值为0)
     *    context.shadowColor :阴影的颜色
     *    context.shadowBlur :阴影的模糊范围(值越大越模糊)
     * 16.绘制图像
     *    绘图:context.drawImage(image,x,y,w,h)  (image对象,绘制图像的x坐标,绘制图像的y坐标,绘制图像的宽度,绘制图像的高度)
     *        context.drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh):
     *    图像平铺:context.createPattern(image,type)
     *         no-repeat:不平铺
     *         repeat-x:横方向平铺
     *         repeat-y:纵方向平铺
     *         repeat:全方向平铺
     * 17.图像裁剪
     *    context.clip()只绘制封闭路径区域内的图像,不绘制路径外部图像,用的时候先创建裁剪区域
     * 18.像素处理
     *    获取像素:var imagedata=context.getImageData(sx,sy,sw,sh)
     *    设置像素;context.putImageData(imagedata,dx,dy,dirtyX,dirtyY,dirtyWidth,dirtyHeight)
     * 19.绘制文字
     *    填充文字:context.fillText(text,x,y)  
     *    绘制文本轮廓:context.strokeText(text,x,y)
     * 20.保存canvas文件
     *     canvas.toDataURL(MIME)
     * 21.保存和状态恢复
     *    context.save() 状态保存,和游戏存档类似
     *    context.restore() 状态恢复,恢复save时的状态
     */

    // 第三种方式,触发点击事件
    // 绘制矩形
    function drawRect() {
      var canvas = document.getElementById('rect')
      if (canvas == null) {
        return false
      }

      var context = canvas.getContext('2d')
      // 默认black
      // context.fillRect(0, 0, 100, 100)
      // context.strokeRect(120, 0, 100, 100)

      // 设置纯色
      context.fillStyle = 'red'
      context.strokeStyle = 'blue'
      context.fillRect(0, 0, 100, 100)
      context.strokeRect(120, 0, 100, 100)
      // 清除矩形
      context.clearRect(25, 25, 100, 50)

      // 加透明度
      // context.fillStyle = 'rgba(255,0,0,0.2)'
      // context.strokeStyle = 'rgba(255,0,0,0.2)'
      // context.fillRect(0, 0, 100, 100)
      // context.strokeRect(120, 0, 100, 100)
    }

    // 绘制圆形
    function drawCircle() {
      var canvas = document.getElementById('circle')
      if (canvas == null) {
        return false
      }

      var context = canvas.getContext('2d')
      context.beginPath()
      context.arc(50, 50, 50, 0, Math.PI * 2, true)
      context.closePath()
      context.fillStyle = 'rgba(0,255,0,0.25)'
      context.fill()
    }

    // 绘制1/4弧线
    function drawTest() {
      const canvas = document.getElementById('test')
      if (canvas == null) {
        return false
      }

      var context = canvas.getContext('2d')
      // 左1/4弧线
      context.beginPath()
      context.arc(0, 50, 50, 0, Math.PI / 2, false)
      // context.fillStyle = 'rgba(255,0,0,0.25)'
      // context.fill()
      context.strokeStyle = 'rgba(255,0,0,0.25)'
      context.stroke()
      context.closePath()

      // 右1/4弧线
      context.beginPath()
      context.arc(100, 50, 50, 0, Math.PI / 2, false)
      // context.fillStyle = 'rgba(255,0,0,0.25)'
      // context.fill()
      context.strokeStyle = 'rgba(255,0,0,0.25)'
      context.stroke()
      context.closePath()
    }

    // 绘制线段
    function drawMoveLine() {
      const canvas = document.getElementById('moveLine')
      if (canvas == null) {
        return false
      }

      var context = canvas.getContext('2d')
      context.strokeStyle = 'red'
      context.fillStyle = 'blue'
      context.lineTo(0, 100)
      context.lineTo(100, 0)
      context.stroke()
    }

    // 利用path和line绘制图案 + 贝塞尔曲线的尝试
    function drawFlower() {
      var canvas = document.getElementById('flower')
      if (canvas == null) {
        return false
      }
      var context = canvas.getContext('2d')
      context.fillStyle = '#EEEEFF'
      context.fillRect(0, 0, 200, 400)
      var n = 0
      var dx = 100
      var dy = 75
      var s = 50
      context.beginPath()
      context.fillStyle = 'blue'
      context.strokeStyle = 'red'
      var x = Math.sin(0)
      var y = Math.cos(0)
      var dig = (Math.PI / 15) * 11
      // +bezier
      // context.moveTo(dx, dy)

      for (var i = 0; i < 30; i++) {
        var x = Math.sin(i * dig)
        var y = Math.cos(i * dig)
        context.lineTo(dx + x * s, dy + y * s)
        // +bezier
        // context.bezierCurveTo(dx + x * s, dy + y * s - 50, dx + x * s + 50, dy + y * s, dx + x * s, dy + y * s);
      }
      context.closePath()
      context.fill()
      context.stroke()
    }

    // 绘制贝塞尔曲线
    function drawBezier() {
      const canvas = document.getElementById('bezier')
      if (canvas == null) {
        return false
      }

      const context = canvas.getContext('2d')
      context.moveTo(25, 25)
      context.bezierCurveTo(25, 25, 75, 25, 75, 75)
      context.stroke()
      context.quadraticCurveTo(75, 125, 125, 125)
      context.stroke()
    }

    // 绘制线性渐变
    function drawGradient() {
      const canvas = document.getElementById('gradient')
      if (canvas == null) {
        return false
      }

      const context = canvas.getContext('2d')
      var lg = context.createLinearGradient(0, 0, 0, 300)
      lg.addColorStop(0, 'rgb(255,0,0)') //红
      lg.addColorStop(0.5, 'rgb(0,255,0)') //绿
      lg.addColorStop(1, 'rgb(0,0,255)') //蓝
      context.fillStyle = lg
      context.fillRect(0, 0, 200, 100)
    }

    // 绘制径向渐变
    function drawGradient1() {
      const canvas = document.getElementById('gradient1')
      if (canvas == null) {
        return false
      }

      const context = canvas.getContext('2d')
      var rg = context.createRadialGradient(50, 50, 0, 50, 50, 50)
      // var rg = context.createRadialGradient(50, 100, 10, 150, 100, 50)
      rg.addColorStop(0.1, 'red')
      rg.addColorStop(0.5, 'orange')
      rg.addColorStop(1, 'yellow')
      context.fillStyle = rg
      // 绘制一个径向渐变的圆
      context.beginPath()
      context.arc(50, 50, 50, 0, Math.PI * 2, true)
      context.closePath()
      context.fill()
      // 绘制一个径向渐变的切面
      // context.fillRect(0, 0, 200, 100)
    }

    // 图形变形
    function drawTrans() {
      const canvas = document.getElementById('trans')
      if (canvas == null) {
        return false
      }

      const context = canvas.getContext('2d')
      context.save()
      context.fillStyle = 'rgb(255,0,0)'
      context.fillRect(0, 0, 30, 30)
      context.translate(50, 50)
      context.scale(0.5, 0.5)
      context.rotate(Math.PI / 4)
      context.fillRect(0, 0, 50, 50)

      context.restore()

      context.save()
      context.fillStyle = 'rgba(255,0,0,0.5)'
      context.scale(0.8, 0.8)
      context.rotate(Math.PI / 6)
      context.translate(120, 0)
      context.fillRect(0, 0, 50, 50)
      context.restore()
    }

    // 图形组合
    function drawComposite() {
      const canvas = document.getElementById('composite')
      if (canvas == null) {
        return false
      }

      const context = canvas.getContext('2d')
      const options = new Array(
        'source-over',
        'destination-over',
        'source-in',
        'destination-in',
        'source-out',
        'destination-out',
        'source-atop',
        'destination-atop',
        'lighter',
        'xor',
        'copy'
      )
      var i = 0
      var interval = setInterval(function () {
        if (i == 10) {
          i = 0
        } else {
          i++
        }
        // 绘制矩形
        context.fillStyle = 'blue'
        context.fillRect(10, 10, 60, 60)
        // 设置组合方式
        context.globalCompositeOperation = options[i]
        // 绘制圆形
        context.beginPath()
        context.fillStyle = 'red'
        context.arc(60, 60, 30, 0, Math.PI * 2, false)
        context.fill()
      }, 1000)
    }

    // 绘制阴影
    function drawShadow() {
      const canvas = document.getElementById('shadow')
      if (canvas == null) {
        return false
      }

      const context = canvas.getContext('2d')
      context.shadowOffsetX = 10
      context.shadowOffsetY = 10
      context.shadowColor = 'rgba(100,100,100,0.2)'
      context.shadowBlur = 1.2
      // 长方形阴影
      context.fillStyle = 'rgba(255,0,0,0.5)'
      context.fillRect(0, 0, 100, 50)
      // 五角星阴影
      // context.translate(100, 50);
      for (var i = 0; i < 3; i++) {
        context.translate(30, 30)
        createStar(context)
        context.fill()
      }
    }

    function createStar(context) {
      var n = 0
      var dx = 50
      var dy = 0
      var s = 25
      context.beginPath()
      context.fillStyle = 'rgba(255,0,0,0.5)'
      var x = Math.sin(0)
      var y = Math.cos(0)
      var dig = (Math.PI / 5) * 4
      for (var i = 0; i < 5; i++) {
        var x = Math.sin(i * dig)
        var y = Math.cos(i * dig)
        context.lineTo(dx + x * s, dy + y * s)
      }
      context.closePath()
    }

    // 绘图
    function paintImage() {
      const canvas = document.getElementById('image')
      if (canvas == null) {
        return false
      }

      const context = canvas.getContext('2d')
      var image = new Image()
      image.src = './html.gif'
      context.fillStyle = '#EEEEFF'
      context.fillRect(0, 0, 200, 100)
      // image.onload = function () {
      //   context.drawImage(image, 0, 0);
      // }

      // image.onload = function () {
      //   context.drawImage(image, 50, 50, 100, 50)
      // }

      image.onload = function () {
        context.drawImage(image, 10, 10, 200, 100, 10, 10, 180, 80)
      }
    }

    // 设置图片平铺方式
    function drawRepeat() {
      const canvas = document.getElementById('repeat')
      if (canvas == null) {
        return false
      }

      const context = canvas.getContext('2d')
      var image = new Image()
      image.src = './pet.jpg'
      var types = ['no-repeat', 'repeat-x', 'repeat-y', 'repeat']
      var i = 0
      image.onload = function () {
        var interval = setInterval(function () {
          context.clearRect(0, 0, 400, 300)
          if (i >= 4) {
            i = 0
          }
          var ptrn = context.createPattern(image, types[i])
          context.fillStyle = ptrn
          context.fillRect(0, 0, 400, 300)
          i++
        }, 1000)
      }
    }

    // 图像裁剪
    function drawClip() {
      const canvas = document.getElementById('clip')
      if (canvas == null) {
        return false
      }

      const context = canvas.getContext('2d')
      context.fillStyle = 'orange'
      context.fillRect(0, 0, 300, 200)
      image = new Image()
      image.onload = function () {
        drawImage(context, image)
      }
      image.src = './html.gif'
    }

    function drawImage(context, image) {
      // 圆形裁剪区
      // createCircleClip(context)
      // 星形裁剪区
      createStarClip(context)
      context.drawImage(image, 0, 0)
    }

    function createCircleClip(context) {
      context.beginPath()
      context.arc(100, 100, 50, 0, Math.PI * 2, true)
      context.closePath()
      context.clip()
    }

    function createStarClip(context) {
      var n = 0
      var dx = 100
      var dy = 50
      var s = 50
      context.beginPath()
      var x = Math.sin(0)
      var y = Math.cos(0)
      var dig = (Math.PI / 5) * 4
      for (var i = 0; i < 5; i++) {
        var x = Math.sin(i * dig)
        var y = Math.cos(i * dig)
        context.lineTo(dx + x * s, dy + y * s)
      }
      context.closePath()
      context.clip()
    }

    // 像素处理
    function drawHandlePx() {
      console.log('drawHandlePx');

      // const canvas = document.getElementById('handlePx')
      // if (canvas == null) {
      //   return false
      // }

      // const context = canvas.getContext('2d')
      // context.fillStyle = 'red'
      // context.fillRect(0, 0, 300, 200)
      // var image = new Image()
      // image.src = './html.gif'
      // image.onload = function () {
      //   context.drawImage(image, 0, 0, 200, 100)
      //   var imagedata = context.getImageData(0, 0, 400, 300)

      //   // 修改imagedata
      //   // getImageData这个函数,必须在服务器端运行,如果没有服务器环境(比如,只是一个本地的 html网页,操作本地的一张图片),就会报错
      //   for (var i = 0, n = imagedata.data.length; i < n; i += 4) {
      //     imagedata.data[i + 0] = 255 - imagedata.data[i + 0] //red;
      //     imagedata.data[i + 1] = 255 - imagedata.data[i + 1] //green
      //     imagedata.data[i + 2] = 255 - imagedata.data[i + 2] //blue
      //     //imagedata.data[i + 3] = 255 - imagedata.data[i + 3]; //a
      //   }
      //   context.putImageData(imagedata, 0, 0)
      // }
    }

    // 绘制文字
    function drawText() {
      const canvas = document.getElementById('text')
      if (canvas == null) {
        return false
      }

      const context = canvas.getContext('2d')
      context.fillStyle = '#EEEEFF'
      context.fillRect(0, 0, 400, 300)
      context.fillStyle = '#00f'
      context.font = 'italic 24px sans-serif'
      context.textBaseline = 'top'
      var text = 'hello world 这是样例文本'
      context.fillText(text, 0, 0)
      var length = context.measureText(text)
      context.fillText('长' + length.width + 'px', 0, 50)
      context.font = "bolid 22px sans-serif";
      text = "love and peace 测试二";
      length = context.measureText(text);
      context.strokeText(text, 0, 100);
      context.fillText("长" + length.width + "px", 0, 150);
    }

    // 保存文件
    function drawSave() {
      const canvas = document.getElementById('save')
      if (canvas == null) {
        return false
      }

      const context = canvas.getContext('2d')
      var rg = context.createRadialGradient(50, 50, 0, 50, 50, 50)
      rg.addColorStop(0.1, 'red')
      rg.addColorStop(0.5, 'orange')
      rg.addColorStop(1, 'yellow')
      context.fillStyle = rg
      context.shadowOffsetX = 10
      context.shadowOffsetY = 10
      context.shadowColor = 'rgba(100,100,100,0.5)'
      context.shadowBlur = 1.2
      context.beginPath()
      context.arc(50, 50, 50, 0, Math.PI * 2, true)
      context.closePath()
      context.fill()

      function download() {
        let type = 'png'
        let img_png_src = canvas.toDataURL('image/png')
        let imgData = img_png_src.replace(imgType(type), 'image/octet-stream')
        let filename = '图片' + '.' + type
        saveFile(imgData, filename)
      }

      let saveFile = function (data, fileName) {
        let save_link = document.createElement('a')
        save_link.href = data
        save_link.download = fileName
        let event = document.createEvent('MouseEvents')
        event.initEvent('click', true, false)
        save_link.dispatchEvent(event)
      }

      function imgType(ty) {
        let type = ty.toLowerCase().replace(/jpg/i, 'jpeg')
        var r = type.match(/png|jpeg|bmp|gif/)[0]
        return 'image/' + r
      }

      // 下载图片
      download()
    }

    // const oBtn = document.getElementsByClassName('btn')
    // 第一种方式,触发点击事件
    // oBtn.onclick = function () {
    //   alert('点击 one')
    // }

    // 第二种方式,触发点击事件,注意通过name获取class对象要加数组0
    // oBtn[0].addEventListener('click', function () {
    //   alert('点击 two')
    // })
  </script>
</body>

</html>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值