背景
我在忙碌且枯燥且乏味日子里,突然有那么一天,产品经理提了这么一个需求:由于算法引擎想提升效率,本来由他们截取的图片现在由前端去截取。我突然感觉,我的摸鱼日子要被打破了。终于有了一个有意思的需求了。
我盯着需求盯了5分钟,陷入了沉思。又沉思了3分钟。。。我不会。
可是产品经理提的,总不能直接说不做吧。这也太有失我前端five形象了!
我又陷入了沉思。。突然,我沙包一样的脑袋里突然蹦出了一个词:canvas
对啊,canva!然后我就去 MDN 里搜索了相关的API,确实可行。那就抄起我的24k纯金键盘一把梭吧。
初步实现
通过查阅MDN文档,canvas
首先需要一个画布空间
<canvas id="canvas"></canvas>
接着javascript
部分需要获取这个元素的引用,并且获得元素的context,图像将会在此完成渲染。
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ok,到这一步,canvas
初始化的步骤已经完成。但是我们处理的,是图像呀。所以,我们需要初识化image
对象。
// 初始化image对象
const img = new Image();
// 本地环境下,带有域名的图片地址存在跨域,设置crossOrigin,允许跨域
img.setAttribute("crossOrigin", "Anonymous");
img.src = url;
img.onload = function () {
// TODO
};
现在,所有的步骤都以初识化完成。接下来就是把两个步骤结合起来。
// 初始化image对象
const img = new Image();
// 本地环境下,带有域名的图片地址存在跨域,设置crossOrigin,允许跨域
img.setAttribute("crossOrigin", "Anonymous");
img.src = url;
img.onload = function () {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) {
console.error("cannot init screenshot canvas");
return;
}
// TODO
};
这里之所以不采用创建HTML的结构的原因是我们这个是react呀,怎么能用这么low的方式呢
基本完成
现在我们来看一下canvas怎么实现截图功能。canvas提供了两个API:
drawImage()
toDataURL
drawImage
用于绘制图像,toDataURL
用于导出图像。具体怎么用请移步 MDN 查看。
有人可能会问,canvas实现裁剪有专门的API
clip
,为什么不用呢?
首先,clip
这个方法确实能裁剪图片,但是需要创建裁剪路径。当然,对于复杂图形,比如五角星这样图形,用clip
这个方式会更好,但是我们这里只处理矩形这一种情况。所以如果想更深入的改进这个方法,可以自行研究补充。
现在我们根据点位,来赋给canvas所要裁剪的大小。点位如图所示。
红色标注出来的即为点位。接下来,就是计算drawImage
所需要的所有参数了
let dx = 0;
let dy = 0;
let width = 0;
let height = 0;
dx = _.get(points, "[0][0]", 0);
dy = _.get(points, "[0][1]", 0);
const ex = _.get(points, "[1][0]", 0);
const ey = _.get(points, "[1][1]", 0);
width = ex - dx;
height = ey - dy;
到此为止,所有的工作都完成啦,奖励自己一颗小星星 ✨
究极封装
我们既然用react,当然得把这些代码react化啦,hooks当之无愧!以下,就是全部代码:
import { useState } from "react";
import _ from "lodash";
export const useCutImg = (url: string | undefined, points: number[][]) => {
const [result, setResult] = useState<string | undefined>();
if (points.length > 3 || points.length < 1) {
console.error("points's length should be 2");
return [""];
}
if (!url) {
console.error("can not find image url");
return [""];
}
let dx = 0;
let dy = 0;
let width = 0;
let height = 0;
dx = _.get(points, "[0][0]", 0);
dy = _.get(points, "[0][1]", 0);
const ex = _.get(points, "[1][0]", 0);
const ey = _.get(points, "[1][1]", 0);
width = ex - dx;
height = ey - dy;
const img = new Image();
// 本地环境下,带有域名的图片地址存在跨域,设置crossOrigin,允许跨域
img.setAttribute("crossOrigin", "Anonymous");
img.src = url;
img.onload = function () {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) {
console.error("cannot init screenshot canvas");
return;
}
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, dx, dy, width, height, 0, 0, width, height);
const data = canvas.toDataURL("image/png");
setResult(data);
};
return [result];
};
最后
封装完成后,用法就很简单了,引入这个hooks,然后
const [result] = useCutImg('xxx.img',[[200,100],[1000,800]])
谢谢大家看到这里!觉得有用的,可以点赞收藏哟,笔芯~