diff --git a/packages/web/CHANGELOG.md b/packages/web/CHANGELOG.md index e5ca836f..1cd1f56b 100644 --- a/packages/web/CHANGELOG.md +++ b/packages/web/CHANGELOG.md @@ -1,5 +1,9 @@ # @pear-rec/web +## 1.3.13 + +feat: 首页增加编辑动图 + ## 1.3.12 fix: 编辑动图初始化渲染bug diff --git a/packages/web/src/components/card/editGifCard.tsx b/packages/web/src/components/card/editGifCard.tsx new file mode 100644 index 00000000..3ee7583c --- /dev/null +++ b/packages/web/src/components/card/editGifCard.tsx @@ -0,0 +1,58 @@ +import React, { useImperativeHandle, forwardRef, useRef } from 'react'; +import { useTranslation } from 'react-i18next'; +import { EditOutlined, GifOutlined } from '@ant-design/icons'; +import { Space, Card, Dropdown, Modal } from 'antd'; + +const EditGifCard = forwardRef((props: any, ref: any) => { + const { t } = useTranslation(); + const fileRef = useRef(null); + + function handleUploadFile(event) { + const file = event.target.files[0]; + if (window.electronAPI) { + window.electronAPI.sendEiOpenWin({ imgUrl: file.path }); + } else { + const imgUrl = window.URL.createObjectURL(file); + Modal.confirm({ + title: '提示', + content: `是否编辑${file.name}`, + okText: t('modal.ok'), + cancelText: t('modal.cancel'), + onOk() { + window.open(`/editGif.html?imgUrl=${encodeURIComponent(imgUrl)}`); + }, + }); + } + event.target.value = ''; + } + + function handleClipScreenClick(type) { + if (window.isElectron) { + window.electronAPI.sendCsOpenWin({ type }); + window.electronAPI.sendMaCloseWin(); + } else { + location.href = `/recorderScreen.html?type=${type}`; + } + } + + return ( + + fileRef.current.click()}> + {t('home.edit')} + +
+ handleClipScreenClick('gif')} /> +
{t('home.gif')}
+
+ +
+ ); +}); + +export default EditGifCard; diff --git a/packages/web/src/components/card/viewImageCard.tsx b/packages/web/src/components/card/viewImageCard.tsx index b9bb39cd..8abf128f 100644 --- a/packages/web/src/components/card/viewImageCard.tsx +++ b/packages/web/src/components/card/viewImageCard.tsx @@ -6,17 +6,18 @@ import { Space, Card, Dropdown, Modal } from 'antd'; const ViewImageCard = forwardRef((props: any, ref: any) => { useImperativeHandle(ref, () => ({ - handleViewImage, + // handleViewImage, })); const { t } = useTranslation(); const fileRef = useRef(null); + const imgRef = useRef(null); const directoryRef = useRef(null); - function handleViewImage(e: any) { - window.electronAPI ? window.electronAPI.sendViOpenWin() : (location.href = '/viewImage.html'); - e.stopPropagation(); - } + // function handleViewImage(e: any) { + // window.electronAPI ? window.electronAPI.sendViOpenWin() : (location.href = '/viewImage.html'); + // e.stopPropagation(); + // } const onClick: MenuProps['onClick'] = ({ key }) => { if (key == 'file') { @@ -57,6 +58,25 @@ const ViewImageCard = forwardRef((props: any, ref: any) => { event.target.value = ''; } + function handleUploadImg(event) { + const file = event.target.files[0]; + if (window.electronAPI) { + window.electronAPI.sendEiOpenWin({ imgUrl: file.path }); + } else { + const imgUrl = window.URL.createObjectURL(file); + Modal.confirm({ + title: '提示', + content: `是否编辑${file.name}`, + okText: t('modal.ok'), + cancelText: t('modal.cancel'), + onOk() { + window.open(`/editImage.html?imgUrl=${encodeURIComponent(imgUrl)}`); + }, + }); + } + event.target.value = ''; + } + function handleUploadDirectory(event) { const file = event.target.files[0]; if (window.electronAPI) { @@ -67,6 +87,9 @@ const ViewImageCard = forwardRef((props: any, ref: any) => { return ( + imgRef.current.click()}> + {t('home.edit')} +
@@ -83,6 +106,13 @@ const ViewImageCard = forwardRef((props: any, ref: any) => { className="fileRef" onChange={handleUploadFile} /> + { gifBlobRef.current = blob; - const gifUrl = URL.createObjectURL(blob); - gifRef.current.src = gifUrl; }); gif.on('progress', (progress) => { diff --git a/packages/web/src/i18n/locales/de-DE.json b/packages/web/src/i18n/locales/de-DE.json index 1a927c4e..74659d09 100644 --- a/packages/web/src/i18n/locales/de-DE.json +++ b/packages/web/src/i18n/locales/de-DE.json @@ -14,7 +14,10 @@ "viewImage": "Bild anzeigen", "playAudio": "Audio abspielen", "watchVideo": "Video abspielen", - "history": "Verlauf" + "history": "Verlauf", + "editImg": "edit image", + "gif": "gif", + "edit": "edit" }, "editImage": { "save": "speichern" @@ -74,4 +77,4 @@ "close": "schließen", "download": "Download" } -} \ No newline at end of file +} diff --git a/packages/web/src/i18n/locales/en-US.json b/packages/web/src/i18n/locales/en-US.json index fab02af9..7efce2aa 100644 --- a/packages/web/src/i18n/locales/en-US.json +++ b/packages/web/src/i18n/locales/en-US.json @@ -19,7 +19,9 @@ "playAudio": "play audio", "watchVideo": "watch video", "history": "history", - "editImg": "edit image" + "editImg": "edit image", + "gif": "gif", + "edit": "edit" }, "editImage": { "save": "save" @@ -82,4 +84,4 @@ "openServer": "open server", "serverPath": "server path" } -} \ No newline at end of file +} diff --git a/packages/web/src/i18n/locales/zh-CN.json b/packages/web/src/i18n/locales/zh-CN.json index bbc1bf50..c78d8907 100644 --- a/packages/web/src/i18n/locales/zh-CN.json +++ b/packages/web/src/i18n/locales/zh-CN.json @@ -19,7 +19,9 @@ "playAudio": "查看音频", "watchVideo": "查看视频", "history": "历史", - "editImg": "修改图片" + "editImg": "编辑图片", + "gif": "动图", + "edit": "编辑" }, "editImage": { "save": "保存" @@ -82,4 +84,4 @@ "openServer": "打开服务", "serverPath": "服务地址" } -} \ No newline at end of file +} diff --git a/packages/web/src/pages/editGif/index.module.scss b/packages/web/src/pages/editGif/index.module.scss index 068d1edb..9f0cfa50 100644 --- a/packages/web/src/pages/editGif/index.module.scss +++ b/packages/web/src/pages/editGif/index.module.scss @@ -45,7 +45,9 @@ position: relative; img { padding: 5px; - height: calc(100% - 15px); + margin: 0 5px; + height: calc(100% - 16px); + border: 1px solid #ccc; } .info { position: absolute; @@ -61,7 +63,7 @@ -1px 1px 1px black; } } - .current { + .current img { background-color: rgb(203 232 246); } } diff --git a/packages/web/src/pages/editGif/index.tsx b/packages/web/src/pages/editGif/index.tsx index 061d4664..1954ad45 100644 --- a/packages/web/src/pages/editGif/index.tsx +++ b/packages/web/src/pages/editGif/index.tsx @@ -34,8 +34,55 @@ const EditGif = () => { const searchParams = new URLSearchParams(paramsString); let videoUrl = searchParams.get('videoUrl'); let filePath = searchParams.get('filePath'); + let imgUrl = searchParams.get('imgUrl'); - videoUrl ? loadVideo(videoUrl) : loadVideoFrames(filePath); + videoUrl && loadVideo(videoUrl); + filePath && loadVideoFrames(filePath); + imgUrl && loadImg(imgUrl); + } + + const fetchImageByteStream = async (imgUrl: string) => { + const response = await fetch(imgUrl); + return response.body!; + }; + + const createImageDecoder = async (imageByteStream: ReadableStream) => { + const imageDecoder = new (window as any).ImageDecoder({ + data: imageByteStream, + type: 'image/gif', + }); + await imageDecoder.tracks.ready; + await imageDecoder.completed; + return imageDecoder; + }; + + async function loadImg(imgUrl) { + const imageByteStream = await fetchImageByteStream(imgUrl); + const imageDecoder = await createImageDecoder(imageByteStream); + const { image: imageFrame } = await imageDecoder.decode({ frameIndex: 0 }); + const frameCount = imageDecoder.tracks.selectedTrack!.frameCount; + const frameDuration = imageFrame.duration! / 1000; + let _videoFrames = []; + + for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) { + const result = await imageDecoder.decode({ frameIndex }); + await saveImg(result.image, frameIndex); + result.image.close(); + } + setVideoFrames(_videoFrames); + + async function saveImg(videoFrame, frameIndex) { + const canvas = new OffscreenCanvas(videoFrame.displayWidth, videoFrame.displayHeight); + const context = canvas.getContext('2d'); + context.drawImage(videoFrame, 0, 0); + const blob = await canvas.convertToBlob({ type: 'image/jpeg' }); + const url = URL.createObjectURL(blob); + _videoFrames.push({ + url: url, + filePath: frameIndex, + index: frameIndex, + }); + } } function loadVideo(videoUrl) { diff --git a/packages/web/src/pages/home/index.tsx b/packages/web/src/pages/home/index.tsx index e25783a0..db808ad8 100644 --- a/packages/web/src/pages/home/index.tsx +++ b/packages/web/src/pages/home/index.tsx @@ -8,7 +8,7 @@ import RecordAudioCard from '../../components/card/recordAudioCard'; import ViewImageCard from '../../components/card/viewImageCard'; import ViewVideoCard from '../../components/card/viewVideoCard'; import ViewAudioCard from '../../components/card/viewAudioCard'; -import EditImageCard from '../../components/card/editImageCard'; +import EditGifCard from '../../components/card/editGifCard'; import ininitApp from '../../pages/main'; import { useUserApi } from '../../api/user'; import styles from './index.module.scss'; @@ -107,7 +107,7 @@ const Home: React.FC = () => { - +