夏日拍照必备之Canvas 裁剪图片为九宫格🏖

861 阅读1分钟

我正在参加「初夏创意投稿大赛」详情请看:初夏创意投稿大赛


本文使用 react 作为框架。样式和布局比较简单,就不细说啦,代码放到最后。

基础布局

import { useEffect, useState } from 'react';
import './App.css';

const canvasArr = [];
for (let i = 1; i < 10; i++) {
  canvasArr.push(i);
}

function App(props) {

  return (
    <div className="App">
      <div className="left">
        <div className="my-canvas">
          <canvas width="300" height="300" id="mycnavas"></canvas>
        </div>
        <div className="new-photo">
          开始切图
        </div>
      </div>
      <div className="right">
        <div className="new-canvas">
          {
            canvasArr.map((i) => <canvas width="100" height="100" id={`img${i}`} key={i}></canvas>)
          }
        </div>
        <div className="download">
          下载图片
        </div>
      </div>
    </div>
  );
}

export default App;

图片.png

在 useEffect 的时候,初始化 canvas。

同时为右边的 canvas 添加显示的判断条件。当点击按钮的时候,才会显示出来。

但是这里为什么我们要使用 style 进行 display 的判断呢?这是因为避免后续使用右侧的 canvas 时找不到元素而报错。

// ...
function App(props) {
  const [context, setContext] = useState(null);
  const [show, setShow] = useState(false);

  const init = () => {
    const canvas = document.getElementById('mycnavas');
    const cxt = canvas.getContext("2d");
    setContext(cxt);
    const img = new Image();
    img.crossOrigin = "anonymous";
    img.src = props.img;
    window.onload = function () {
      cxt.drawImage(img, 0, 0, 400, 300);
    };
  }

  useEffect(() => {
    init();
  }, []);

  return (
    <div className="App">
      <div className="right" style={{ 'display': show ? 'block' : 'none' }}>
        <div className="new-canvas">
          {
            canvasArr.map((i) => <canvas width="100" height="100" id={`img${i}`} key={i}></canvas>)
          }
        </div>
        <div className="download">
          下载图片
        </div>
      </div>
    </div>
  );
}

export default App;

canvas 有两个方法:

通过 getImageData() 复制画布上指定矩形的像素数据,然后通过 putImageData() 将图像数据放回画布

示例代码:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.fillStyle = "red";
ctx.fillRect(10,10,50,50);

function copy() {
  var imgData = ctx.getImageData(10,10,50,50);
  ctx.putImageData(imgData,10,70);
}

点击开始切图的时候,调用 handleClick 方法

// ...

function App(props) {
  // ...
  const handleClick = () => {
    let q = 1;
    for (let i = 0; i < 3; i++) {
      for (let j = 0; j < 3; j++) {
        const data = context.getImageData(j * 100, i * 100, 400, 100);
        const img = document.getElementById(`img${q}`);
        const newCxt = img.getContext('2d');
        newCxt.putImageData(data, 0, 0);
        arr.push(img.toDataURL(`${q}.png`));
        q++;
      }
    }
    setShow(true);
  }
  
  // ...

  return (
    <div className="App">
      <div className="left">
        <div className="new-photo" onClick={handleClick}>
          开始切图
        </div>
      </div>
    </div>
  );
}

export default App;

图片.png

还不错吧,还差最后一步,下载图片就完成啦啦啦啦啦

// ...

function App(props) {
  // ....
  const handleDownLoad = () => {
    for (let i = 1; i < 10; i++) {
      const a = document.createElement('a');
      a.download = `img${i}`;
      a.href = arr[i];
      document.body.appendChild(a);
      a.click();
    }
  }
  
  // ...

  return (
    <div className="App">
      <div className="right" style={{ 'display': show ? 'block' : 'none' }}>
        <div className="download" onClick={handleDownLoad}>
          下载图片
        </div>
      </div>
    </div>
  );
}

export default App;

图片.png

App.css

.App {
  text-align: center;
  display: flex;
}

.left {
  flex: 1;
}

.right {
  flex: 1;
}

canvas {
  border: 1px solid;
  margin: 3px;
}

.new-canvas {
  width: 350px;
  height: 316px;
  margin: 0px auto 20px;
}

.new-photo,
.download {
  width: 300px;
  height: 40px;
  line-height: 40px;
  margin: auto;
  background-color: cornflowerblue;
  border-radius: 5px;
  cursor: pointer;
  margin: 10px auto;
  color: white;
  margin-top: 10px;
}

App.js

import { useEffect, useState } from 'react';
import './App.css';

const canvasArr = [];
for (let i = 1; i < 10; i++) {
  canvasArr.push(i);
}

function App(props) {
  const [show, setShow] = useState(false);
  const [arr, setArr] = useState([]);
  const [context, setContext] = useState(null);

  const init = () => {
    const canvas = document.getElementById('mycnavas');
    const cxt = canvas.getContext("2d");
    setContext(cxt);
    const img = new Image();
    img.crossOrigin = "anonymous";
    img.src = props.img;
    window.onload = function () {
      cxt.drawImage(img, 0, 0, 400, 300);
    };
  }

  const handleClick = () => {
    let q = 1;
    for (let i = 0; i < 3; i++) {
      for (let j = 0; j < 3; j++) {
        const data = context.getImageData(j * 100, i * 100, 400, 100);
        const img = document.getElementById(`img${q}`);
        const newCxt = img.getContext('2d');
        newCxt.putImageData(data, 0, 0);
        arr.push(img.toDataURL(`${q}.png`));
        q++;
      }
    }
    setShow(true);
  }

  const handleDownLoad = () => {
    for (let i = 1; i < 10; i++) {
      const a = document.createElement('a');
      a.download = `img${i}`;
      a.href = arr[i];
      document.body.appendChild(a);
      a.click();
    }
  }

  useEffect(() => {
    init();
  }, []);

  return (
    <div className="App">
      <div className="left">
        <div className="my-canvas">
          <canvas width="300" height="300" id="mycnavas"></canvas>
        </div>
        <div className="new-photo" onClick={handleClick}>
          开始切图
        </div>
      </div>
      <div className="right" style={{ 'display': show ? 'block' : 'none' }}>
        <div className="new-canvas">
          {
            canvasArr.map((i) => <canvas width="100" height="100" id={`img${i}`} key={i}></canvas>)
          }
        </div>
        <div className="download" onClick={handleDownLoad}>
          下载图片
        </div>
      </div>
    </div>
  );
}

export default App;