Next.js使用filerobot-image-editor

想在工具站里集成一下图片编辑工具,找了一圈发现filerobot-image-editor相当不错。

按照nextjs的官方文档来的,什么 lazy, dymanic 都用上了,还是嗝屁了,最后还是直接用 cdn js+浏览器端 js 解决不把模块引入了, 记录一下解法。

最后的效果见: Image Editor | merchmindai

弯路无非就是 canvas 怎么处理,dynamic 导入使用 SSR:false,之后不能直接使用 module 内其他变量不然会说找不到默认导入… …
折腾了好一会儿先用React component再用VanillaJS的方式,都因为 server 端处理有问题全部嗝屁,最后想着纯客户端 js 呗,也别进 bundle 了,直接用 cdn 的 js,没有任何依赖问题。

最后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
"use client";

import { useEffect, useRef, useState } from "react";

interface ImageEditorProps {
source?: string;
}

const socialMediaCrops = [
{
titleKey: "facebook",
items: [
{
titleKey: "profile",
width: 180,
height: 180,
descriptionKey: "180x180",
},
],
},
];

const ImageEditor = ({ source }: ImageEditorProps) => {
const editorRef = useRef<HTMLDivElement>(null);
const [isEditorReady, setIsEditorReady] = useState(false);

useEffect(() => {
let intervalId = null;
const checkFilerobotEditor = () => {
if (
typeof window !== "undefined" &&
window.FilerobotImageEditor !== undefined
) {
if (intervalId !== null) {
clearInterval(intervalId);
}
setIsEditorReady(true);
}
};

// Check immediately in case it's already loaded
checkFilerobotEditor();

// Set up a listener to check periodically
intervalId = setInterval(checkFilerobotEditor, 500);

// Clean up the interval when the component unmounts
return () => clearInterval(intervalId);
}, []);

useEffect(() => {
if (!isEditorReady || !source) {
return;
}
console.log("init", isEditorReady, source);
const initializeEditor = async () => {
const FilerobotImageEditor = window.FilerobotImageEditor;
const TABS = window.FilerobotImageEditor.TABS;
const TOOLS = window.FilerobotImageEditor.TOOLS;

const config = {
source: source,
onSave: (editedImageObject: any, designState: any) => {
let tmpLink = document.createElement("a");
tmpLink.href = editedImageObject.imageBase64;
tmpLink.download = editedImageObject.fullName;
tmpLink.style = "position: absolute; z-index: 100; visibility: none;";
document.body.appendChild(tmpLink);
tmpLink.click();
document.body.removeChild(tmpLink);
console.log("saved", editedImageObject, designState);
},
annotationsCommon: {
fill: "#ff0000",
},
Text: { text: "Filerobot..." },
Rotate: { angle: 90, componentType: "slider" },
translations: {
profile: "Profile",
coverPhoto: "Cover photo",
facebook: "Facebook",
socialMedia: "Social Media",
fbProfileSize: "180x180px",
fbCoverPhotoSize: "820x312px",
},
Crop: {
autoResize: true,
presetsItems: [
{
titleKey: "classicTv",
descriptionKey: "4:3",
ratio: 4 / 3,
},
{
titleKey: "cinemascope",
descriptionKey: "21:9",
ratio: 21 / 9,
},
],
presetsFolders: [
{
titleKey: "socialMedia",
groups: socialMediaCrops,
},
],
},
tabsIds: [
TABS.ADJUST,
TABS.ANNOTATE,
TABS.WATERMARK,
TABS.FILTERS,
TABS.FINETUNE,
],
defaultTabId: TABS.ADJUST,
defaultToolId: TOOLS.TEXT,
};

if (editorRef.current) {
const filerobotImageEditor = new FilerobotImageEditor(
editorRef.current,
config
);

filerobotImageEditor.render({
// onClose: (closingReason: string) => {
// console.log("Closing reason", closingReason);
// filerobotImageEditor.terminate();
// },
});
}
};

initializeEditor();

return () => {
// Cleanup function (if needed)
};
}, [source, isEditorReady]);

return (
<>
{source ? (
<div
id="editor_container"
ref={editorRef}
style={{ height: "750px", width: "1200px" }}
></div>
) : null}
</>
);
};

export default ImageEditor;

具体实现很简单,外部的 page 引入 CDN 的 js <Script src="https://scaleflex.cloudimg.io/v7/plugins/filerobot-image-editor/latest/filerobot-image-editor.min.js" />
这段 js 本身在初始化完毕之后会往window对象写入FilerobotImageEditor, 在组件内检查是否完成初始化,后面就执行对应的创建即可。

其他可以用 cdn js 初始化的都可以这么用,反正在浏览器环境都是跑的 js。

↑ TOP