老妹每年都会帮妈妈画春联,
今年人在国外,拍胸脯保证说会画电子档给她!
呕心沥血画了两天後,妈很满意,但画了两天就只有这样也太亏了吧?
(手画春联我可是不用5分钟一张)
於是乎自己用React+Fabric.js做了这个新年图制作机~
可以快速把亲朋好友图片上传,当Line贺年用啦
下面这不是我妈是网路素人
去年就想玩fabric.js
长辈图制作,就沿用去年已经用bootstrap写好的版型
fabric.js是一个Canvas函式库,可以让Canvas上的物件产生互动
然後练习一下刚学会的React,新手上路,请多多包涵
浏览器中文的话会切回中文
很贴心画了三款,还可以变身成老虎招财猫
还没有新年图的可以到下面玩玩看~(初二都要过了不需要了吧!)
目前遇到的问题&没有的功能:(有时间再解决...)
-手机版动不了!(Fabric.js无法拖拉图片动不了)
-第一次进画面好像图片无法拖拉动不了(原因不明)重新整理後就可以
-即时Resize暂时无法即时侦测,不确定该怎麽设计各个canvas大小...所以目前都固定,只有手机和电脑板尺寸(还有bug就先抱歉了?)
因为React+Fabric.js网路上资料有限(Fabric.js的官方文件又太好读了呵呵)
自己查找资料,想梳理一下自己的过程,也想看看有没有前辈可以给一些意见 > <
说明一下自己的开发内容:
主要元件
- Canvas 本体,主要操作逻辑 //NewYearCanvas.js档案
- 剪裁图片的弹跳视窗 //CustomModal档案
开发顺序
- React & Bootstrap & Fabric.js 起手式
- 设定 Canvas 背景
a. 读取src
b. 设定长宽- 上传图片
a. Modal开启
b. 上传图片&读取- 剪裁图片
a. 添加Clip Path
b. 裁切- 添加裁切好图片到Canvas上
- 添加贴纸 (跟上一步骤5差不多)
- 移动顺序
- 下载图片
前言写太长了,这一篇我只会写到3
下载要用的东西
//已经create-react-app
npm install --save bootstrap //使用Bootstrap样式
npm install --save react-bootstrap //要使用bootstrap的元件
npm install fabric
在主要的档案里面引用
//NewYearCanvas.js档案
import React, {useEffect, useState } from "react";
import { fabric } from "fabric";
import 'bootstrap/dist/css/bootstrap.min.css';
import './NewYearCanvas.css'; //客制化样式要盖过Bootstrap
import CustomModal from "./CustomModal"; //後面上传用的Modal
import {Form} from "react-bootstrap"; //上传图片要用的组件
起手式
使用useEffect Hook, 在第一次渲染时执行Fabic & Canvas的起手式
//NewYearCanvas.js
const NewYearCanvas = (props)=>{
const [canvas, setCanvas] = useState('')
useEffect(()=>{
let canvasWidth = 500;
//new fabric.Canvas('你设的canvas id')
//要正方形的图片
const canvas = new fabric.Canvas('canvas', {
width: canvasWidth,
height: canvasWidth
})
//将Canvas存在state里面
setCanvas(canvas)
}, [])
}
export default NewYearCanvas
为了不让底图也可以修改,造成图层太多太复杂,决定把自己的图档当成Canvas的背景
很像图片编辑软件Template的概念
右上方点击图片之後就可以换成背景图~
利用Fabirc的 canvas.setBackgroundImage()
函式设定背景
//NewYearCanvas.js
const NewYearCanvas = (props)=>{
//部份省略
//图片src的部分我是从App.js用props传进来的
const renderBgImages = ()=>{
return props.bgImages.map(image=>{
return (
<img onClick={()=>setBg(image.src)}
role="button"
key={image.alt}
src={image.src}
alt={image.alt} />
)
})
}
//点击就把src变成参数丢进来
//fabric.Image 为fabric添加图片的用法
//设成背景:canvas.setBackgroundImage(img)
const setBg = (src)=>{
if(!canvas) return
fabric.Image.fromURL(src, function (img) {
img.scaleToWidth(canvas.width); //最後设宽度
canvas.setBackgroundImage(img);
canvas.requestRenderAll();
});
}
return (
//...部份省略
<div>
<canvas id="canvas"></canvas>
</div>
<div>{renderBgImages()}</div>
//部份省略
)
}
export default NewYearCanvas
上传图片主要是为了让我的亲朋好友们可以上传自己的图像,
然後剪成圆形,还可以带我画的虎帽!
为了不让本来的Canvas太复杂,另开弹跳视窗让使用者上传图片~
另做了一个组件:CustomModal.js 使用Boostrap Modal元件
细节部分(标题等等)都用props
传进来
//CustomModal.js
import Reactfrom "react";
import {Modal} from "react-bootstrap";
const CustomModal = (props)=>{
return(
<Modal size={props.size} show={props.show} onHide={props.handleClose}>
<Modal.Header closeButton>
<Modal.Title>{props.title}</Modal.Title>
</Modal.Header>
<Modal.Body style={{overflow: 'scroll'}}>
{props.children}
</Modal.Body>
<Modal.Footer>
<button onClick={props.resetModal} type="button" className="btn btn-secondary"
data-bs-dismiss="modal">{props.resetText}</button>
{renderSaveButton()}
</Modal.Footer>
</Modal>
)
}
export default CustomModal
上传档案也使用bootstrap form
元件,变成props的小孩塞到CustomModal.js里面~
欸等等,Modal用的canvas也要初始化啊!
既然都在NewYearCanvas.js就一样使用useEffect初始化他
然後也用state存起来方便等一下呼叫!
//NewYearCanvas.js
import CustomModal from "./CustomModal";
const NewYearCanvas = (props)=>{
//...以上省略
const [canvasModal, setCanvasModal] = useState('')
useEffect(()=>{
const canvasWidth = 500;
const canvasModal = new fabric.Canvas('canvasModal', {
width: canvasWidth,
height: canvasWidth,
})
setCanvasModal(canvasModal)
},[show])
//中间省略
return (
//props太长了省略不写
<CustomModal
size="lg">
//上传档案部分
<Form.Group onChange={uploadPhoto}>
//只接受图片类型
<Form.Control type="file" id="imageUpload" accept="image/*"/>
</Form.Group>
//modal用的canvasModal, id要不一样
<canvas id="canvasModal" className="mx-auto"></canvas>
</CustomModal>
)
}
export default NewYearCanvas
然後监听onChange,呼叫uploadPhoto 去处理上传的图片档案
这张图片呢,主要是给大家切割用,照理说应该放成背景图的样子,
可是背景图又不能切割!所以还是做成fabric.js的图片档案,但让他不能选取、
不能移动、啥都不能做!所以在做图片时,把hasControls等参数设定为false
//NewYearCanvas.js
import CustomModal from "./CustomModal";
const NewYearCanvas = (props)=>{
const [uploadImage, setUploadImage] = useState('')
//...以上省略
const uploadPhoto = (e)=>{
//创建一个图片并且存取上传的档案的src
const uploadImageTmp = new Image();
uploadImageTmp.src = URL.createObjectURL(e.target.files[0]);
uploadImageTmp.onload = function () {
//做一个fabric Image物件,并设定参数
const image = new fabric.Image(uploadImageTmp);
image.set({
left: 0,
top: 0,
clipPath: '',
hasControls: false, //图片我不想让他可以变形会动
lockMovementX: true,//图片也不能移动
lockMovementY: true,
"selectable": false,//图片也不能选取
"evented": false//图片不成任何事件的目标
});
//先把图片设成Canvas的宽度
//再把Canvas的图片变跟图形一样高
//这是我个人喜好啦!但这样图片会变小
image.scaleToWidth(canvasModal.width);
canvasModal.setHeight(image.height*image.scaleY)
//一样存在state里面
setUploadImage(image)
//把图片加进去
canvasModal.add(image).renderAll();
}
}
}
export default NewYearCanvas
真的还菜菜的!
有术语上的错误、资料结构和写法有任何建议请不吝指教!
文章不会写到太多细节,原始码放在这边:https://github.com/rachel-liaw/react_canvas
下篇不知何时会写...(远目)
大家新年快乐!?
<<: DE2_115(DAY1)以niosii去控制板子上的led灯
>>: DE2_115(DAY2)用niosii和switch还有NiosII console去控制板子上的led
& 父选择器“&”通常与Sass的Nesting用法搭配使用, 当内层的选择器使用&...
什麽是状态机呢? 状态机,其实是有限状态机(finite-state machine(FSM))的简...
Azure cognitive service: Text-to-Speech- Azure 念给你...
Virtual Judge ZeroJudge 题意 输入十进位的数字,输出对应的费氏进位表示法 ...
前言一 因为一些原因,今天应该是我铁人赛的完赛日。完成了 20%,剩下的 80%,就留给大家去阅读更...