iT邦幫忙

1

虎你發財啦!自己的新年圖自己做 (React+Fabric.js) -下

  • 分享至 

  • xImage
  •  

前情提要

為了不浪費我白白畫的春聯,做了一個新年圖製造機
還沒有新年圖的可以到下面玩玩看~(快收假了不需要了吧!為什麼我的GA沒有數據QQ)

虎你快樂新年圖製作機

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

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

(還有bug就先抱歉了?)

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

開發內容:

主要元件

  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. 下載圖片

4. 剪裁圖片

步驟:加上剪裁範圍→剪裁
先來加上剪裁範圍


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. 算出剪裁圓的左上角

https://ithelp.ithome.com.tw/upload/images/20220204/20140247Uh7KcgB7wK.png

這邊直接說明:

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



5. 添加切好圖片到Canvas上

把圖片裁切好之後,想要直接加上到Canvas上,

但問題來了,圖片被裁切掉的地方也會以透明方式出現,很難操作

https://ithelp.ithome.com.tw/upload/images/20220204/20140247fpVLNLEZZ8.png

我自己想到的方式是把在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

6. 添加貼紙 & 7. 移動順序

貼紙一樣用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();

    }


8.下載圖片

把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邏輯等等...)

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

/images/emoticon/emoticon41.gif

過年後,就要把它變成我的快速圖片製造機(?)
再來加上繪圖功能、拖曳功能......(碎碎念)


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

2 則留言

0
吹雪
iT邦新手 5 級 ‧ 2023-02-08 16:36:53

写的真好 点赞/images/emoticon/emoticon07.gif

0
吹雪
iT邦新手 5 級 ‧ 2023-02-17 14:16:53

发现一个问题诶 如果待裁剪的区域旋转了一下的话 这个裁剪功能就出bug了。。

我要留言

立即登入留言