大家好,我是開源圖片編輯器的 https://github.com/ikuaitu/vue-fabric-editor 的作者,它是一款基于 PC 版本的開源圖片編輯器。
最近很多開發者咨詢,是否可以將開源圖片編輯器改造為一款適用于移動端的 H5 版本圖片編輯器,最近 H5 版本的圖片編輯器剛剛上線,就將實現思路和產品細節整理成筆記分享出來,供大家參考。
基礎
開源的圖片編輯器的基本功能都有了,例如切換模板、添加元素、自定義字體等,不過相較于移動端的交互會有很大的差異,做了很多改造,這次筆記主要分享一下移動端圖片編輯器實現思路和細節。
大綱
- 切換模板
- 添加圖片
- 添加組合元素
- 設置背景色
- 修改畫布尺寸
- 快捷菜單
- 屬性工具條
- 特效字體
- 切換字體
- 輸入文字
- 文字排版
- 邊框
- 陰影
- 下載圖片
注:部分代碼示例為封裝后的代碼,非 fabric.js 原生方法。
1. 切換模板
編輯器基于 fabric.js 開發,所有的模板都是以 json 的格式存儲,切換模板只需要請求詳情接口,將 json 格式的數據調添加到畫布當中即可,需要注意的點是需要將模板中使用的字體名稱,并加載字體文件后再進行渲染,否則字體樣式沒辦法正常渲染。
const loadInfo = async (res: any) => {
const info = res.data
templName.value = info.name;
await canvasEditor.getFontList(JSON.stringify(info.json));
canvasEditor.loadJSON(JSON.stringify(info.json), () => LoadingPlugin(false));
};
2. 添加圖片
fabric.js 中添加圖片提供了很多種方法,我們使用通過最簡單的fabric.Image.fromURL
即可,另外,經常有圖片尺寸大于畫布的情況,還需要將圖片按畫布寬度的一般進行縮放,更方便用戶操作。
const toEditor = async (e: MouseEvent) => {
visible.value = false
LoadingPlugin(true)
const item = await canvasEditor.createImgByElement(e.target as HTMLImageElement)
await canvasEditor.addBaseType(item, { scale: true })
LoadingPlugin(false)
}
3. 添加組合元素
fabric.js 支持將單個元素按照 JSON 格式導出/導入,我們將導出的數據存儲在數據庫中的,導入時按元素類型導入即可,需要獲取 JSON 中元素的類型,并作為方法名調用,同樣需要在導入前做字體加載,倒入后做縮放。
const capitalizeFirstLetter = (str: string) => {
return str.charAt(0).toUpperCase() + str.slice(1);
}
const toEditor = async (item: ItemProps) => {
visible.value = false
LoadingPlugin(true)
await canvasEditor.downFontByJSON(JSON.stringify(item.json));
const el = JSON.parse(JSON.stringify(item.json));
const elType = capitalizeFirstLetter(el.type);
new fabric[elType].fromObject(el, (fabricEl: fabric.Object) => {
canvasEditor.dragAddItem(fabricEl);
LoadingPlugin(false)
});
}
4. 設置背景色
設置背景色較為簡單,按照 fabric.js 的 API 設置顏色即可,需要注意的是大部分 PC 端的顏色組件并不適配移動端 H5 的場景,不支持 touch 事件,我們使用了 @jaames/iro
這個組件,它在移動端表現出色,完全適配我們的場景,而且它的 API 很靈活,我們將它封裝成一個通用的顏色組件,在多處調用。
<template>
<div ref="pickerContainer">
</div>
</template>
<script setup lang="ts">
import iro from '@jaames/iro';
const emit = defineEmits(['update:modelValue', 'change']);
const props = defineProps({
modelValue: {
type: String,
default: '#000000'
},
width: {
type: Number,
default: 200
}
});
const pickerContainer = ref<HTMLButtonElement | string>('');
let colorPicker: any = null;
onMounted(() => {
colorPicker = iro.ColorPicker(pickerContainer.value, {
width: props.width,
color: props.modelValue,
borderWidth: 1,
borderColor: "#fff",
layoutDirection: 'horizontal',
layout: [
{
component: iro.ui.Slider,
options: {
id: 'hue-slider',
sliderType: 'hue'
}
},
{
component: iro.ui.Box,
},
{
component: iro.ui.Slider,
options: {
sliderType: 'alpha'
}
}
]
});
colorPicker.on('color:change', (color: any) => {
const rgbaString = color.rgbaString;
emit('update:modelValue', rgbaString);
emit('change', rgbaString);
});
});
</script>
5. 修改畫布尺寸
日常使用圖片編輯器都有修改畫布尺寸的需要,在開源項目中已經封裝好了相應的方法,直接調用即可,需要注意的是,當修改尺寸彈框彈出時,為了達到所見即所得的效果,要避免彈框遮擋畫布,其他屬性修改同理。
const resizeEditor = async () => {
await nextTick()
const editorWorkspase = document.querySelector('#workspace') as HTMLElement
const popElement = document.querySelector('.my-editor-popup') as HTMLElement
const headerElement = document.querySelector('.t-navbar') as HTMLElement
if (popElement) {
editorWorkspase.style.height = `calc(100vh - ${popElement?.offsetHeight + headerElement?.offsetHeight || 0}px)`
} else {
editorWorkspase.style.height = ''
}
}
6. 快捷菜單
很多快捷操作需要能夠讓用戶快速找的并完成操作,我們為元素添加了快捷菜單功能,避免讓一些簡單的操作讓用戶在底部菜單欄點來點去,當選中元素時自動展示,取消選中時隱藏即可,需要注意的是在快捷菜單并不總是在元素上方,快捷菜單應該根據元素位置和畫布的尺寸進行定位,當菜單超出畫布區域時我們要及時調整菜單位置;另外 當屬性彈框出現,畫布尺寸變化時,需要同步修改菜單位置。
const upDatePosition = async () => {
const activeObject = canvasEditor.canvas.getActiveObject();
if (activeObject) {
canvasEditor.canvas.renderAll();
fixLeft.value = 10;
fixTop.value = 10;
await nextTick();
isIncluded(activeObject);
await nextTick();
}
}
getObjectAttr(upDatePosition)
canvasEditor.canvas.on('selection:updated', upDatePosition)
canvasEditor.canvas.on('mouse:move', upDatePosition)
canvasEditor.on('workspaceAutoEvent', upDatePosition)
7. 屬性工具條
參考了其他圖片編輯器,部分屬性在點擊元素后才會出現可修改選項,取消選中時便隱藏選項,另外 選中的元素不同,可修改選項也不同,這是一個在移動端做復雜圖片編輯器中非常棒的一個交互。
我們封裝了通用的選中類型和方法,針對每個屬性組件單獨設置隱藏/展示。
8. 特效字體
特效字體主要是文字元素的顏色、邊框、陰影的組合,我們將來文字設置樣式后的 JSON 導出并保存在數據庫中,當選中某一個特效時,將屬性按 JSON 中的數據設置給元素即可。
const setStyle = (item: ImgItem) => {
const activeObject = canvasEditor.canvas.getActiveObjects()[0];
if (activeObject) {
const values = toRaw(item.json);
const keys = ['fill', 'stroke', 'strokeWidth', 'shadow', 'strokeLineCap'];
activeObject.set('paintFirst', 'stroke');
keys.forEach((key) => {
activeObject.set(key, values[key]);
if (key === 'fill' && typeof values[key] != 'string') {
activeObject.set(key, new fabric.Gradient(values[key]));
}
});
canvasEditor.canvas.renderAll();
}
};
9. 切換字體
修改字體只需要調用 fabric.js 元素的fontFamily
屬性即可,在修改之前要確保字體加載完成。
const changeCommon = async (key: string, value: any) => {
const activeObject = canvasEditor.canvas.getActiveObjects()[0];
if (activeObject) {
LoadingPlugin(true);
baseAttr.fontFamily = value;
try {
await canvasEditor.loadFont(value)
} catch (error) {
console.log(error)
}
LoadingPlugin(false);
activeObject && activeObject.set(key, value);
canvasEditor.canvas.renderAll();
}
};
10. 輸入文字
fabric.js 可直接雙擊文字元素進行修改,不過在移動端這種交互并不醒目,我們單獨為文本元素進行了修改,選中元素后,再次點擊時彈出輸入框,可以在底部菜單欄點擊按鈕進行修改。
11. 文字排版
文字排版較為簡單,我們只需要按照 fabric.js 的文字屬性對文字進行屬性設置即可,如 fontSize、lineHeight、charSpacing 等。
const baseAttr = reactive({
fontSize: 0,
lineHeight: 0,
charSpacing: 0,
textAlign: '',
fontWeight: '',
fontStyle: '',
underline: false,
linethrough: false,
overline: false,
});
12. 邊框
邊框樣式和文字樣式類似,配合顏色組件可以很快捷的實現功能。
const baseAttr = reactive({
stroke: '#fff',
strokeWidth: 0,
strokeDashArray: [],
});
13. 陰影
引用屬性主要是元素的 shadow 子屬性的修改,代碼如下:
const baseAttr = reactive({
shadow: {
color: '#fff',
blur: 0,
offsetX: 1,
offsetY: 1,
}
});
const changeCommon = () => {
const activeObject = canvasEditor.canvas.getActiveObjects()[0];
if (activeObject) {
activeObject.set('shadow', new fabric.Shadow(baseAttr.shadow));
canvasEditor.canvas.renderAll();
}
};
14. 下載圖片
fabric.js 可以導出 Png/Jpeg/Base64 格式的圖片,同時 JPEG 格式還可以指定圖片質量與尺寸倍數,詳見 fabric.js 的 API 文檔。
結尾
以上就是 fabric.js 開發移動端編輯器的實現細節了,結合我們的開源項目和插件化架構可以很方便的完成項目開發,如果你在做類似項目或者做類似的項目,歡迎與我交流。
開源項目:https://github.com/ikuaitu/vue-fabric-editor/blob/main/README-zh.md
?轉自https://www.cnblogs.com/nihaojob/p/18426386
該文章在 2024/12/10 9:03:32 編輯過