iT邦幫忙

5

虎你快樂啦!自己的新年圖自己做 (React+Fabric.js) -上

  • 分享至 

  • xImage
  •  

老妹每年都會幫媽媽畫春聯,
今年人在國外,拍胸脯保證說會畫電子檔給她!

嘔心瀝血畫了兩天後,媽很滿意,但畫了兩天就只有這樣也太虧了吧?
(手畫春聯我可是不用5分鐘一張)
於是乎自己用React+Fabric.js做了這個新年圖製作機~
可以快速把親朋好友圖片上傳,當Line賀年用啦

下面這不是我媽是網路素人
虎你快樂啦

去年就想玩fabric.js長輩圖製作,就沿用去年已經用bootstrap寫好的版型

fabric.js是一個Canvas函式庫,可以讓Canvas上的物件產生互動

然後練習一下剛學會的React,新手上路,請多多包涵

瀏覽器中文的話會切回中文
https://ithelp.ithome.com.tw/upload/images/20220202/20140247ejjp4Nq4Rk.png

很貼心畫了三款,還可以變身成老虎招財貓
還沒有新年圖的可以到下面玩玩看~(初二都要過了不需要了吧!)

虎你快樂新年圖製作機

目前遇到的問題&沒有的功能:(有時間再解決...)

-手機版動不了!(Fabric.js無法拖拉圖片動不了)
-第一次進畫面好像圖片無法拖拉動不了(原因不明)重新整理後就可以
-即時Resize暫時無法即時偵測,不確定該怎麼設計各個canvas大小...所以目前都固定,只有手機和電腦板尺寸

(還有bug就先抱歉了?)

因為React+Fabric.js網路上資料有限(Fabric.js的官方文件又太好讀了呵呵)
自己查找資料,想梳理一下自己的過程,也想看看有沒有前輩可以給一些意見 > <
說明一下自己的開發內容:

主要元件

  1. Canvas 本體,主要操作邏輯 //NewYearCanvas.js檔案
  2. 剪裁圖片的彈跳視窗 //CustomModal檔案

開發順序

  1. React & Bootstrap & Fabric.js 起手式
  2. 設定 Canvas 背景
    a. 讀取src
    b. 設定長寬
  3. 上傳圖片
    a. Modal開啟
    b. 上傳圖片&讀取
  4. 剪裁圖片
    a. 添加Clip Path
    b. 裁切
  5. 添加裁切好圖片到Canvas上
  6. 添加貼紙 (跟上一步驟5差不多)
  7. 移動順序
  8. 下載圖片

前言寫太長了,這一篇我只會寫到3
/images/emoticon/emoticon37.gif


1. React & Bootstrap & Fabric.js 起手式

下載要用的東西

//已經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

2. 設定Canvas背景

為了不讓底圖也可以修改,造成圖層太多太複雜,決定把自己的圖檔當成Canvas的背景
很像圖片編輯軟體Template的概念
右上方點擊圖片之後就可以換成背景圖~

https://ithelp.ithome.com.tw/upload/images/20220202/20140247ejjp4Nq4Rk.png

利用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

3. 上傳圖片

上傳圖片主要是為了讓我的親朋好友們可以上傳自己的圖像,
然後剪成圓形,還可以帶我畫的虎帽!

為了不讓本來的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

真的還菜菜的!
有術語上的錯誤、資料結構和寫法有任何建議請不吝指教!

/images/emoticon/emoticon41.gif

文章不會寫到太多細節,原始碼放在這邊:https://github.com/rachel-liaw/react_canvas
下篇不知何時會寫...(遠目)
大家新年快樂!?


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
wolfwang
iT邦研究生 4 級 ‧ 2022-02-07 18:01:43

感謝分享,新年快樂

謝謝!!/images/emoticon/emoticon08.gif

0
Vader
iT邦研究生 4 級 ‧ 2022-02-09 16:57:14

好像很好玩的一個小專題

自己可以用的小專題/images/emoticon/emoticon07.gif

我要留言

立即登入留言