以下,來說說故事,這是一個使用 fabric.js + Next.js(React) 製作的專案
剛開始對技術選用與 React、fabric.js 底層理解都不夠深,過程處理可能聽起來不太聰明,但都是我走過的路QAQ
結果未必是最好的解法。單純經驗分享,如果有更好的方式都歡迎討論~
初期的時候,我也是像前面介紹的那些功能那樣把需求的功能一個一個拼起來。
在無框架環境上,這些看起來是單純些。
在單純環境下,知道這些功能怎麼運作、組裝。
但是,我們的需求是要做在 next.js / React 的專案裡面
看到人家用
'use client';
import React, { useRef, useEffect } from 'react';
import { fabric } from 'fabric';
const FabricCanvas = () => {
const fabricRef = useRef<null | fabric.Canvas>(null);
const canvasRef = useRef<null | HTMLCanvasElement>(null);
useEffect(() => {
const initFabric = () => {
if (!canvasRef.current) return;
fabricRef.current = new fabric.Canvas(canvasRef?.current, {
width: 800,
height: 600,
backgroundColor: '#fff',
});
fabricRef.current.renderAll();
};
initFabric();
const disposeFabric = () => {
fabricRef?.current?.dispose();
};
return () => {
disposeFabric();
};
}, []);
return <canvas ref={canvasRef} />;
};
export default FabricCanvas;
然後才能引入 FabricCanvas 到頁面來用
import FabricCanvas from './FabricCanvas';
const page = () => {
return (
<div>
<FabricCanvas />
</div>
);
};
export default page;
當時覺得很複雜而且有很多東西要處理,所以就去找了別人的套件來用(掩面)
殊不知,這是錯誤的開始...
fabricjs-react - npm (npmjs.com)
哇,看起來變得簡單多了
直接引入,畫布會幫你從 <FabricJSCanvas />
這裡做好,還有 onReady
供你在畫布準備好時做一些想做的處理
警語:
使用套件前請先了解套件內容的運作方式,不要無腦使用套件。
套件本身沒有錯,錯的是把它用在不符合需求,而需要大改才好用的地方。
現在回來看,這套件適合在很簡單、快速創建簡單功能的地方使用。
仔細看他除了 canvas 外,還幫你寫了一些現成可用,不用再另外設參數就可以使用的 api
(據我的理解) 他的 editor 這個參數,是用 state 的方式儲存,並用 hook 的方式來集中管理處理 editor 的 function
但把這些放到 next.js / React 的環境裡面運作、拆分到不同 component 使用時,問題就出現了。
一開始想用 Redux 來做整個畫布的狀態管理,想說有多種狀態,Redux 比較好處理,
但在把整個畫布(也就是套件裡的 editor
)存進 Redux 時卻一直出現一個錯誤訊息
A non-serializable value was detected in the state...
non-serializable 不可序列化?是什麼意思
來舉個例子:
Redux 就像一個堅持只運輸標準箱子的搬家公司:
所以,像你的狗狗就會被搬家公司拒絕運輸,因為他無法保證狗狗不會亂動亂跑或是大便、偷咬東西的行為。
這裡的狗狗就很像我們的畫布本體,可能會在搬家公司非預期的時候改變。
所以被 Redux 以不可序列化(大概等於 不可被裝進靜止箱子裡,並且無法控制此物件的改變)來拒絕我們。
好的,回到我們的畫布
好吧,既然不能用 Redux 來存畫布,那畫布的部分我改成用 useContext 來存吧
未完待續...明天繼續