Skip to content
On this page

进阶Demo

ps调色盘(当前颜色,更换主题色,浮动的圆圈)

ts
<!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" />
    <style>
      #canvas {
        box-shadow: 0px 0px 5px #ccc;
      }
      #canvas-color,
      #canvas-lucid {
        margin-left: 20px;
        box-shadow: 0px 0px 5px #ccc;
      }
    </style>
  </head>
  <body>
    <canvas id="canvas" width="500" height="500"></canvas>
    <canvas id="canvas-color" width="40" height="500"></canvas>
    <canvas id="canvas-lucid" width="40" height="500"></canvas>
    <div id="rgb">rgb</div>
    <div id="rgba">rgba</div>
    <div id="hex">hex</div>
    <br />
    <div>设置主题色</div>
    R:<input
      id="r"
      oninput="changInput(event, 'r')"
      type="number"
      min="0"
      max="255"
      style="width: 100px"
    />
    G:<input
      id="g"
      oninput="changInput(event, 'g')"
      type="number"
      min="0"
      max="255"
      style="width: 100px"
    />
    B:<input
      id="b"
      oninput="changInput(event, 'b')"
      type="number"
      min="0"
      max="255"
      style="width: 100px"
    />

    <script>
      const r = document.getElementById("r");
      const g = document.getElementById("g");
      const b = document.getElementById("b");
      r.value = 255;
      g.value = 0;
      b.value = 0;

      function getColor(ctx, x, y) {
        const imageData = ctx.getImageData(x, y, 1, 1);
        const pixel = imageData.data;
        const rs = pixel[0];
        const gs = pixel[1];
        const bs = pixel[2];
        let as = pixel[3] / 255;
        as = Math.round(as * 100) / 100;

        let rHex = rs.toString(16);
        let gHex = gs.toString(16);
        let bHex = bs.toString(16);
        rs < 16 && (rHex = "0" + rHex);
        gs < 16 && (gHex = "0" + gHex);
        bs < 16 && (bHex = "0" + bHex);

        const rgbaColor = "rgba(" + rs + "," + gs + "," + bs + "," + as + ")";
        const rgbColor = "rgb(" + rs + "," + gs + "," + bs + ")";
        const hexColor = "#" + rHex + gHex + bHex;

        setText(rgbColor, rgbaColor, hexColor);
        return [rs, gs, bs];
      }

      function setText(rgb, rgba, hex) {
        const a = document.getElementById("rgb");
        const b = document.getElementById("rgba");
        const c = document.getElementById("hex");
        rgb && (a.textContent = rgb)
        rgba && (b.textContent = rgba)
        hex && (c.textContent = hex)
      }

      function changInput(e, type) {
        const value = Number(e.target.value);
        if (value >= 0 && value <= 255) {
          switch (type) {
            case "r":
              colorObj.rgbx = value;
              break;
            case "g":
              colorObj.rgby = value;
              break;
            case "b":
              colorObj.rgbz = value;
              break;
          }
          // 这里暂时不改 step
          colorObj.drawBg();
        } else {
          e.target.value = "";
        }
      }
    </script>

    <script>
      const canvas = document.getElementById("canvas");
      const ctx = canvas.getContext("2d");

      const colorObj = {
        rgbx: 255,
        rgby: 0,
        rgbz: 0,
        lucid: 1,
        step: 1,
        changeRGB() {
          // 颜色周期变化规则为:
          // 1: 255,0,0
          // 2: 255,255,0
          // 3: 0,255,0
          // 4: 0,255,255
          // 5: 0,0,255
          // 6: 255,0,255
          // 7: 255,0,0
          switch (this.step) {
            case 1:
              this.rgby++;
              if (this.rgby === 255) this.step = 2;
              break;
            case 2:
              this.rgbx--;
              if (this.rgbx === 0) this.step = 3;
              break;
            case 3:
              this.rgbz++;
              if (this.rgbz === 255) this.step = 4;
              break;
            case 4:
              this.rgby--;
              if (this.rgby === 0) this.step = 5;
              break;
            case 5:
              this.rgbx++;
              if (this.rgbx === 255) this.step = 6;
              break;
            case 6:
              this.rgbz--;
              if (this.rgbz === 0) this.step = 1;
              break;
          }
        },
        drawBg() {
          ctx.clearRect(0, 0, 500, 500);

          const gradient1 = ctx.createLinearGradient(0, 0, 500, 0);
          gradient1.addColorStop(0, "#fff");
          gradient1.addColorStop(1, "rgba(0,0,0,0)");
          ctx.fillStyle = gradient1;
          ctx.fillRect(0, 0, 500, 500);

          ctx.globalCompositeOperation = "destination-over";

          ctx.beginPath();
          const gradient2 = ctx.createLinearGradient(0, 0, 0, 500);
          gradient2.addColorStop(
            0,
            `rgb(${this.rgbx}, ${this.rgby}, ${this.rgbz})`
          );
          gradient2.addColorStop(1, "#000000");
          ctx.fillStyle = gradient2;
          ctx.fillRect(0, 0, 500, 500);
        },
        drawBall(x, y) {
          ctx.clearRect(0, 0, 500, 500);
          colorObj.drawBg();
          ctx.beginPath();
          ctx.globalCompositeOperation = "source-over";
          ctx.arc(x, y, 10, 0, Math.PI * 2);
          this.strokeStyle = "white";
          ctx.stroke();
          getColor(ctx, x, y);
        }
      };

      colorObj.drawBg();
      canvas.addEventListener("mousemove", function (e) {
        colorObj.drawBall(e.offsetX, e.offsetY);
      });
    </script>

    <script>
      const canvasColor = document.getElementById("canvas-color");
      const ctxColor = canvasColor.getContext("2d");

      const drawColor = {
        colorArr: [
          "255,0,0",
          "255,255,0",
          "0,255,0",
          "0,255,255",
          "0,0,255",
          "255,0,255",
          "255,0,0",
        ],
        drawBg() {
          const gradient = ctxColor.createLinearGradient(0, 0, 0, 500);
          for (let i = 0; i < this.colorArr.length; i++) {
            gradient.addColorStop(i / this.colorArr.length, `rgb(${this.colorArr[i]})`);
          }
          ctxColor.fillStyle = gradient;
          ctxColor.fillRect(0, 0, 40, 500);
        },
        drawRect(x, y) {
          if (y >= 494 || y <= 0) return;
          ctxColor.clearRect(0, 0, 40, 500);
          this.drawBg();
          ctxColor.beginPath();
          ctxColor.globalCompositeOperation = "source-over";
          ctxColor.strokeRect(0, y, 40, 6);
          ctxColor.strokeStyle = "#000";

          const colorRes = getColor(ctxColor, 20, y - 5);
          colorObj.rgbx = colorRes[0];
          colorObj.rgby = colorRes[1];
          colorObj.rgbz = colorRes[2];
          colorObj.drawBg();

          lucidObj.setRgb(colorRes.join())
        }
      }
      drawColor.drawBg();
      canvasColor.addEventListener("mousemove", function (e) {
        drawColor.drawRect(e.offsetX, e.offsetY);
      });
    </script>

    <script>
      const canvasLucid = document.getElementById("canvas-lucid");
      const ctxLucid = canvasLucid.getContext("2d");
      const lucidObj = {
        rgb: '255,0,0',
        setRgb(rgb) {
          this.rgb = rgb;
          this.drawBg()
        },
        drawBg() {
          const gradient = ctxLucid.createLinearGradient(0, 0, 0, 500);
          gradient.addColorStop(0, `rgb(${this.rgb})`);
          gradient.addColorStop(0.94, `rgb(255,255,255)`);
          ctxLucid.fillStyle = gradient;
          ctxLucid.fillRect(0, 0, 40, 500);
        },
        drawRect(x, y) {
          ctxLucid.clearRect(0, 0, 40, 500);
          this.drawBg();
          ctxLucid.beginPath();
          ctxLucid.globalCompositeOperation = "source-over";
          ctxLucid.strokeRect(0, y, 40, 6);
          ctxLucid.strokeStyle = "#000";

          colorObj.lucid = (500-y)/500;
          const { rgbx, rgby, rgbz, lucid } = colorObj;
          const rgbaColor = `rgba(${rgbx},${rgby},${rgbz},${lucid})`;
          document.getElementById("rgba").textContent = rgbaColor;
        }
      }
      lucidObj.drawBg()
      canvasLucid.addEventListener("mousemove", function (e) {
        lucidObj.drawRect(e.offsetX, e.offsetY);
      });
    </script>
  </body>
</html>

刮刮乐(待完善)

html
<!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" />
    <style>
      #canvas {
        box-shadow: 0px 0px 5px #ccc;
      }
      #canvas-color,
      #canvas-lucid {
        margin-left: 20px;
        box-shadow: 0px 0px 5px #ccc;
      }
    </style>
  </head>
  <body>
    <canvas id="canvas" width="500" height="500"></canvas>
    <canvas id="canvasText" width="500" height="500"></canvas>

    <script>
      const canvasText = document.getElementById("canvasText");
      const ctxText = canvasText.getContext("2d");
      ctxText.font = "40px 微软雅黑";
      ctxText.strokeText("zxczxc", 200, 200);
      ctxText.fill();
    </script>

    <script>
      const canvas = document.getElementById("canvas");
      const ctx = canvas.getContext("2d");

      ctx.beginPath();
      ctx.fillStyle = "red";
      ctx.fillRect(0, 0, 500, 500);

      canvas.addEventListener("mousemove", function (e) {
        ctx.clearRect(e.offsetX, e.offsetY, 50, 50);
      });
    </script>
  </body>

  <style>
    #canvas {
      position: absolute;
      left: 0;
      top: 0;
      z-index: 3;
    }
  </style>
</html>

截图&下载 图片

注意:下载的步骤可以这样写获取下载地址便捷一些 canvas.toBlob(e => { let url = URL.createObjectURL(e) })

html
<!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>
      /* 给画布增加一个阴影和圆角的样式 */
      canvas {
        box-shadow: 0px 0px 5px #ccc;
        border-radius: 8px;
      }
      img {
        width: 250px;
        height: 250px;
        border: 1px solid red;
      }
      #btn {
        margin: 50px 0 50px 50px;
      }
      #btnDown {
        margin-left: 140px;
      }
    </style>
  </head>
  <body>
    <canvas id="canvas" width="250" height="250"> </canvas>
    <img id="img" />
    <div>
      <button id="btn">转化为图片</button>
      <button id="btnDown">下载</button>
      <button id="again">重新执行动画</button>
    </div>

    <script>
      const canvas = document.getElementById("canvas");
      const Img = document.getElementById("img");
      const Btn = document.getElementById("btn");
      const BtnDown = document.getElementById("btnDown");
      const Again = document.getElementById("again");
      const ctx = canvas.getContext("2d");

      const ball = {
        x: 100,
        y: 100,
        vx: 1,
        vy: 3,
        radius: 25,
        color: "blue",
        draw() {
          ctx.beginPath();
          ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
          ctx.fillStyle = this.color;
          ctx.fill();
        },
      };
      function draw() {
        // 用带透明度的矩形代替清空
        ctx.fillStyle = "rgba(255, 255, 255, 0.3)";
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ball.draw();
        // 添加加速度
        ball.vy *= 0.995;
        ball.vy += 0.15;
        // 添加速率
        ball.x += ball.vx;
        ball.y += ball.vy;
        // 添加边界
        if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
          ball.vy = -ball.vy;
        }
        if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
          ball.vx = -ball.vx;
        }
        window.requestAnimationFrame(draw);
      }
      window.requestAnimationFrame(draw);
      ball.draw();

      Again.addEventListener(
        "click",
        () => {
          ball.x = 100;
          ball.y = 100;
        },
        false
      );

      Btn.addEventListener(
        "click",
        () => {
          const url = canvas.toDataURL("image/png");
          Img.src = url;
        },
        false
      );

      BtnDown.addEventListener(
        "click",
        () => {
          const url = Img.src;
          if (!url) return;
          const arr = url.split(",");
          const mime = arr[0].match(/:(.*?);/)[1]; // 此处得到的为文件类型
          const bstr = atob(arr[1]); // 此处将base64解码
          let n = bstr.length;
          const u8arr = new Uint8Array(n);
          while (n--) {
            u8arr[n] = bstr.charCodeAt(n); // 得到 UTF-16 字符串(例如 a -> 97)
          }

          const file = new File([u8arr], "filename", { type: mime });
          const aDom = document.createElement("a"); // 创建一个 a 标签
          aDom.download = file.name; // 设置文件名
          const href = URL.createObjectURL(file); // 得到本地获取 file 的链接
          aDom.href = href; // 放入href
          document.body.appendChild(aDom); // 将a标签插入 body
          aDom.click(); // 触发 a 标签的点击

          document.body.removeChild(aDom); // 移除刚才插入的 a 标签
          URL.revokeObjectURL(href); // 释放刚才生成的 UTF-16 字符串 (释放了则通过生成的 href 是拿不到图片了的)
        },
        false
      );
    </script>
  </body>
</html>

反相颜色

html
<!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">
  <style>
    /* 给画布增加一个阴影和圆角的样式 */
    canvas {
      box-shadow: 0px 0px 5px #ccc;
      border-radius: 8px;
      float: left;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width="1000" height="500">
  </canvas>
  <button id="original">原本</button>
  <button id="grayscale">黑白</button>
  <button id="inverted">反相</button>
  <script>
    // 获取 canvas 元素
    const canvas = document.getElementById('canvas')
    const originalEl = document.getElementById('original')
    const grayscaleEl = document.getElementById('grayscale')
    const invertedEl = document.getElementById('inverted')
    const ctx = canvas.getContext('2d')

    const img = new Image()
    img.crossOrigin = 'anonymous' // 设置可以读取跨域图片的像素信息
    img.src = 'https://t7.baidu.com/it/u=1819248061,230866778&fm=193&f=GIF'

    img.onload = () => {
      ctx.drawImage(img, 0, 0)
      drawImg.original = () => {
        ctx.drawImage(img, 0, 0)
      }
    }

    const drawImg = {
      original() {},
      grayscale() {
        this.original()
        const img = ctx.getImageData(0, 0, canvas.width, canvas.height)
        const data = img.data
        for (let i = 0; i < data.length; i += 4) {
          const arg = (data[i] + data[i + 1] + data[i + 2]) / 3
          data[i] = arg
          data[i + 1] = arg
          data[i + 2] = arg
        }
        ctx.putImageData(img, 0, 0)
      },
      inverted() {
        this.original()
        const img = ctx.getImageData(0, 0, canvas.width, canvas.height)
        const data = img.data
        for (let i = 0; i < data.length; i += 4) {
          data[i] = 255 - data[i]
          data[i + 1] = 255 - data[i + 1]
          data[i + 2] = 255 - data[i + 2]
        }
        ctx.putImageData(img, 0, 0)
      }
    }

    originalEl.addEventListener('click', () => {
      drawImg.original()
    })
    grayscaleEl.addEventListener('click', () => {
      drawImg.grayscale()
    })
    invertedEl.addEventListener('click', () => {
      drawImg.inverted()
    })
  </script>
</body>
</html>

获取图片中的像素数据

html
<!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">
  <style>
    canvas {
      box-shadow: 0px 0px 5px #ccc;
      border-radius: 8px;
    }
  </style>
</head>
<body>
  <div style="display: flex">
    <canvas id="canvas" width="400" height="500"></canvas>
    <div id="bg" style="width: 100px; height: 200px;">划过的颜色</div>
    <div id="clickBg" style="width: 100px; height: 100px;">点击选中的颜色</div>
  </div>
  <script>
    // 获取 canvas 元素
    const canvas = document.getElementById('canvas')
    const bg = document.getElementById('bg')
    const clickBg = document.getElementById('clickBg')
    const ctx = canvas.getContext('2d')

    const img = new Image()
    img.crossOrigin = 'anonymous' // 设置可以读取跨域图片的像素信息
    img.src = 'https://t7.baidu.com/it/u=1819248061,230866778&fm=193&f=GIF'

    img.onload = () => {
      ctx.drawImage(img, 0, 0)
    }

    const drawBG = (x, y, type) => {
      const data = ctx.getImageData(x, y, 1, 1).data
      const rgba = `rgba(${data[0]}, ${data[1]}, ${data[2]}, ${data[3] / 255})`
      bg.textContent = `划过的颜色: ${rgba}`
      bg.style.backgroundColor = rgba

      if (type === 'click') {
        clickBg.textContent = `选中的颜色: ${rgba}`
        clickBg.style.backgroundColor = rgba
      }
    }

    canvas.addEventListener('mousemove', e => {
      drawBG(e.offsetX, e.offsetY, 'move')
    })
    canvas.addEventListener('click', e => {
      drawBG(e.offsetX, e.offsetY, 'click')
    })
  </script>
</body>
</html>

图片-拖动&缩放&旋转

记得加上在缩放时,根据鼠标所在点进行放大缩小

ts
<!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>
</head>

<body>
  <canvas width="600" height="500" style="border: 1px solid #000;" id="canvas"></canvas>
</body>
<script>
  const canvas = document.getElementById('canvas')
  const ctx = canvas.getContext('2d')

  const img = new Image()
  img.src = 'https://t7.baidu.com/it/u=1819248061,230866778&fm=193&f=GIF'
  const icon = new Image()
  icon.src = 'https://cdn.staticaly.com/gh/M-cheng-web/image-provider@main/blog/logo.ocqmygd2csw.webp'
  img.onload = () => {
    icon.onload = () => {
      obj.PO = { x: obj.imgW / 2 + 50, y: obj.imgH / 2 + 50 }
      ctx.translate(obj.PO.x, obj.PO.y)
      draw()
    }
  }

  const obj = {
    initImgW: 200,
    initImgH: 200,
    imgW: 200,
    imgH: 200,
    beginX: 0,
    beginY: 0,
    rotate: 0,
    PO: { x: 0, y: 0 },

    iconW: 30,
    iconH: 30,

    scale: 1,

    canMove: false,
    canRotate: false,
    canScale: false
  }

  const draw = () => {
    // ctx.clearRect(0, 0, canvas.width, canvas.height)
    ctx.clearRect(
      -canvas.width,
      -canvas.height,
      canvas.width * 2,
      canvas.height * 2
    )
    ctx.drawImage(img, -obj.imgW / 2, -obj.imgH / 2, obj.imgW, obj.imgH)
    ctx.drawImage(icon, obj.imgW / 2, -obj.imgH / 2, obj.iconW, obj.iconH)
  }

  const imgIsDown = (x, y) => {
    return (
      -obj.imgW / 2 < x &&
      x < obj.imgW / 2 &&
      -obj.imgH / 2 < y &&
      y < obj.imgH / 2
    )
  }

  const iconIsDown = (x, y) => {
    return (
      obj.imgW / 2 < x &&
      x < obj.imgW / 2 + obj.iconW &&
      -obj.imgH / 2 < y &&
      y < -obj.imgH / 2 + obj.iconH
    )
  }

  // window屏幕坐标转化为canvas坐标
  const convertPX = (x, y) => {
    //在屏幕坐标系中,相对canvas坐标系原点PO的偏移,所以要减去canvas坐标原点
    x = x - obj.PO.x
    y = y - obj.PO.y
    //如果没有旋转,那么只计算偏移量就行,不用考虑角度
    if (obj.rotate != 0) {
      //Math.sqrt是两点之间的距离图中OM的距离,简化版本,正确用法应该是Math.sqrt((x-0)*(x-0) + (y-0)*(y-0))
      const len = Math.sqrt(x * x + y * y)
      //屏幕坐标系中 PO与按下点连线 与屏幕坐标系X轴的夹角弧度
      const oldR = Math.atan2(y, x)
      //canvas坐标系中PO与按下点连线 与canvas坐标系x轴的夹角弧度
      const newR = oldR - obj.rotate
      //最终算出来canvas坐标系上的M点
      x = len * Math.cos(newR)
      y = len * Math.sin(newR)
    }
    return { x, y }
  }

  canvas.onmousedown = e => {
    obj.beginX = e.offsetX
    obj.beginY = e.offsetY
    const Cp = convertPX(obj.beginX, obj.beginY)
    obj.canMove = imgIsDown(Cp.x, Cp.y)
    obj.canRotate = iconIsDown(Cp.x, Cp.y)

    canvas.onmousemove = e => {
      const { offsetX: x, offsetY: y } = e
      if (obj.canMove) {
        const movex = x - obj.beginX
        const movey = y - obj.beginY
        const CP0 = convertPX(movex + obj.PO.x, movey + obj.PO.y)
        ctx.translate(CP0.x, CP0.y)
        draw()
        obj.PO.x += movex
        obj.PO.y += movey
        obj.beginX = x
        obj.beginY = y
      }
      if (obj.canRotate) {
        const CP = convertPX(x, y)
        const Cx = CP.x
        const Cy = CP.y
        //根据坐标算出来旋转的角度,这里减去一个50°是因为可旋转图标在图标的右上角,往上是逆时针,所以要减去他原有的角度,根据可旋转图标的位置来更改这个初始角度
        const newR = Math.atan2(Cx, -Cy) - (50 * Math.PI) / 180
        //旋转canvas画布
        ctx.rotate(newR)
        //记录一下现在的角度
        obj.rotate += newR
        draw()
      }
    }
  }

  canvas.onmousewheel = e => {
    const { offsetX: x, offsetY: y } = e
    const Cp = convertPX(x, y)
    //同样需要判断如果鼠标在图片上,才允许缩放
    obj.canScale = imgIsDown(Cp.x, Cp.y)
    if (obj.canScale) {
      //e.wheelDelta如果大于0,证明鼠标是向上滚动,反之向下
      if (e.wheelDelta > 0) {
        //放大的倍数可以根据实际情况定义,可以丝滑一点
        obj.scale += 0.03
      }
      if (e.wheelDelta < 0) {
        obj.scale -= 0.03
      }
      //不管放大还是缩下,都是用初始的宽高,来放大或者缩小
      obj.imgW = obj.scale * obj.initImgW
      obj.imgH = obj.scale * obj.initImgH
      draw()
    }
  }

  canvas.onmouseup = () => {
    obj.canMove = false
    obj.canRotate = false
    canvas.onmousemove = null
    document.onmouseup = null
  }
</script>

</html>