为了不浪费我白白画的春联,做了一个新年图制造机
还没有新年图的可以到下面玩玩看~(快收假了不需要了吧!为什麽我的GA没有数据QQ)
目前遇到的问题&没有的功能:(有时间再解决...)
-手机版动不了!(Fabric.js无法拖拉图片动不了)
-第一次进画面好像图片无法拖拉动不了(原因不明)重新整理後就可以
-即时Resize暂时无法即时侦测,不确定该怎麽设计各个canvas大小...所以目前都固定,只有手机和电脑板尺寸(还有bug就先抱歉了?)
开发内容:
主要元件
- Canvas 本体,主要操作逻辑 //NewYearCanvas.js档案
- 剪裁图片的弹跳视窗 //CustomModal档案
开发/说明顺序
文章上篇这边请
- React & Bootstrap & Fabric.js 起手式 (文章上篇)
- 设定 Canvas 背景(文章上篇)
a. 读取src
b. 设定长宽- 上传图片(文章上篇)
a. Modal开启
b. 上传图片&读取- 剪裁图片 (<---您的位置在这里)
a. 添加Clip Path
b. 裁切- 添加裁切好图片到Canvas上
- 添加贴纸 (跟上一步骤5差不多)
- 移动顺序
- 下载图片
步骤:加上剪裁范围→剪裁
先来加上剪裁范围
const NewYearCanvas = (props)=>{
const [uploadClipPath, setUploadClipPath] = useState('')
//...以上省略
const addClip = ()=>{
//创造一个fabric的圆形
const userClipPath = new fabric.Circle({
top: 0,
left: 0,
radius: 50,
width: 100,
height: 100,
fill: 'rgb(178, 178, 178, 0.4)',
})
//因为我想要正圆形,所以把可以上拉左拉的控制点都弄掉
userClipPath.setControlsVisibility({
mb: false,
ml: false,
mr: false,
mt: false
})
//加到canvas上面
//setActiveObject(userClipPath) -->让他马上被选取
canvasModal.add(userClipPath).setActiveObject(userClipPath).renderAll();
//存到state里面
setUploadClipPath(userClipPath)
}
//以下省略
}
export default NewYearCanvas
再来剪裁
让使用者随意移动位置,或是放大缩小圆裁切
裁切是利用 Fabric的clipPath参数,制作剪裁遮色片
clipPath的top & left 计算起点是图片中心,所以要先计算出他的位置
再次算出我们拉的圆左上角起始点的位置,和被操作後的半径
这边难点在於计算图片和剪裁圆形 放大缩小後的范围,
1.算出图片中心点 2. 算出剪裁圆的左上角
这边直接说明:
const NewYearCanvas = (props)=>{
//...以上省略
const clipImage = ()=>{
//算出图片中心点起点相对於Canvas座标
//getBoundingRect()取得相对於Canvas位置,但图片都靠左上,就是top0,left0啦
//图片贴上canvas时,我有把他sacale to canvas的大小,
//所以宽度/高度要乘上scale比例後才是正确的宽度高度,然後中心点就是宽高的一半
const imageCenter = {
top: uploadImage.getBoundingRect().top + uploadImage.height * uploadImage.scaleY / 2,
left: uploadImage.getBoundingRect().left + uploadImage.width * uploadImage.scaleX / 2,
}
//stae里面存好的上传图片直接拿出来用,设定剪裁参数
//radius:(半径剪裁圆的半径*放大缩小的scale参数) 除以图片放大缩小参数
//算出剪裁圆起点的位置(相对於图片中心)
// 举top(y座标)为例子:(剪裁圆对Canvas座标y - 图片中心点座标)/(上传图片的缩放比例)
uploadImage.set({
clipPath: new fabric.Circle({
radius: uploadClipPath.radius* uploadClipPath.scaleX / uploadImage.scaleX,
top: (uploadClipPath.getBoundingRect().top - imageCenter
.top) / uploadImage.scaleY,
left: (uploadClipPath.getBoundingRect().left -
imageCenter
.left) / uploadImage.scaleX,
}),
});
canvasModal.renderAll()
}
//以下省略
}
export default NewYearCanvas
如果说明不够清楚欢迎留言!
另外我是参考这位大大的文章:
Day 26 - Fabricjs 进阶自订控制项
https://ithelp.ithome.com.tw/articles/10209056
把图片裁切好之後,想要直接加上到Canvas上,
但问题来了,图片被裁切掉的地方也会以透明方式出现,很难操作
我自己想到的方式是把在Modal上的Canvas宽高都变成跟剪裁的范围一样
先把图片弄到左上角,再直接Canvas换宽度,就不用算图片剪裁厚的座标啦
然後转成图片档案再加上去底层的Canvas
const addPhoto = (stickerSrc)=>{
//先检查是否有被剪裁
if (uploadClipPath) {
//往左上角移动
//移动距离就是剪裁图片的座标,要负的
uploadImage.set({
top: -uploadClipPath.getBoundingRect().top,
left: -uploadClipPath.getBoundingRect().left
})
canvasModal.renderAll()
//置换Canvas的宽高
canvasModal.setDimensions({
width: uploadClipPath.getBoundingRect().width,
height: uploadClipPath.getBoundingRect().height
});
}
//把Canvas转成图片格式
const modifiedImage = canvasModal.toDataURL("image/png").replace("image/png",
"image/octet-stream");
//下面把图片贴上去底层的Canvas(存在state里面)
const pasteImage = new Image();
pasteImage.src = modifiedImage;
pasteImage.onload = function () {
const image = new fabric.Image(pasteImage);
image.set({
left: 100, //这边随意设定
top: 60,
});
canvas.add(image).setActiveObject(image).renderAll();
}
}
//以下省略
}
export default NewYearCanvas
贴纸一样用fabric创造一个image然後贴上去
移动前後顺序才不会有时候虎帽跑到人下面去啦
用fabric的函数: bringToFront()
& sendToBack()
//图片点击就传src回去
<img onClick={()=>addSticker(image.src)}/>
//添加贴纸
const addSticker = ()=>{
const pasteImage = new Image();
pasteImage.src = stickerSrc;
pasteImage.onload = function () {
const image = new fabric.Image(pasteImage);
image.set({
left: 100,
top: 60,
});
canvas.add(image).setActiveObject(image).renderAll();
}
//移动顺序
const setOrder = (order)=>{
//取得正被选取的物件
const obj = canvas.getActiveObject()
if(!obj) return
if(order === 'top') obj.bringToFront()
if(order === 'bottom') obj.sendToBack();
}
把Canvas弄成图片档案,大功告成!
const output = ()=>{
var image = canvas.toDataURL("image/png").replace("image/png",
"image/octet-stream"
);
const a = document.createElement('a')
a.href = image
a.download = `newyear2020.jpeg`
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
}
原始码放在这边:https://github.com/rachel-liaw/react_canvas
不过原始码因为整个操作流程,会比文章多很多逻辑(多了button 开关、DOM逻辑等等...)
还菜菜的!
有术语上的错误、资料结构和写法有任何建议请不吝指教!
过年後,就要把它变成我的快速图片制造机(?)
再来加上绘图功能、拖曳功能......(碎碎念)
<<: Jupyter Notebook 输入栏位设计(2)
A lie would have no sense unless the truth were f...
Elastic Load Balancing 可在多个目标 (例如 Amazon EC2 执行个体、...
库存只剩 1 件,但却有 10 个人买到? 网路商城特卖会常常会推出特定商品限量 1 组的抢购活动...
前言 Array 跟 Object 两兄弟的故事告一段落了,接着是 Object 在外面养(?)的另...
上篇文章我们在 BlueHost 架起了 WordPress 环境,但也许你还不知道什麽是 Word...