mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-20 15:36:18 +00:00
🆙 Camera Fix
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
import { GetRoomCameraWidgetManager, IRoomCameraWidgetEffect, IRoomCameraWidgetSelectedEffect, NitroLogger, RoomCameraWidgetSelectedEffect } from '@nitrots/nitro-renderer';
|
import { GetRoomCameraWidgetManager, IRoomCameraWidgetEffect, IRoomCameraWidgetSelectedEffect, NitroLogger, RoomCameraWidgetSelectedEffect, TextureUtils } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
import { Texture } from 'pixi.js';
|
||||||
import { FaSave, FaSearchMinus, FaSearchPlus, FaTrash } from 'react-icons/fa';
|
import { FaSave, FaSearchMinus, FaSearchPlus, FaTrash } from 'react-icons/fa';
|
||||||
import { CameraEditorTabs, CameraPicture, CameraPictureThumbnail, LocalizeText } from '../../../../api';
|
import { CameraEditorTabs, CameraPicture, CameraPictureThumbnail, LocalizeText } from '../../../../api';
|
||||||
import { Button, Column, Flex, Grid, LayoutImage, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView, Slider, Text } from '../../../../common';
|
import { Button, Column, Flex, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView, Slider, Text } from '../../../../common';
|
||||||
import { CameraWidgetEffectListView } from './effect-list';
|
import { CameraWidgetEffectListView } from './effect-list';
|
||||||
|
|
||||||
export interface CameraWidgetEditorViewProps {
|
export interface CameraWidgetEditorViewProps {
|
||||||
@@ -23,10 +24,18 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
|
|||||||
const [ selectedEffects, setSelectedEffects ] = useState<IRoomCameraWidgetSelectedEffect[]>([]);
|
const [ selectedEffects, setSelectedEffects ] = useState<IRoomCameraWidgetSelectedEffect[]>([]);
|
||||||
const [ effectsThumbnails, setEffectsThumbnails ] = useState<CameraPictureThumbnail[]>([]);
|
const [ effectsThumbnails, setEffectsThumbnails ] = useState<CameraPictureThumbnail[]>([]);
|
||||||
const [ isZoomed, setIsZoomed ] = useState(false);
|
const [ isZoomed, setIsZoomed ] = useState(false);
|
||||||
const [ currentPictureUrl, setCurrentPictureUrl ] = useState<string>('');
|
const [ currentPictureUrl, setCurrentPictureUrl ] = useState<string>(picture?.imageUrl ?? '');
|
||||||
|
const [ stableTexture, setStableTexture ] = useState<Texture>(null);
|
||||||
const debounceTimerRef = useRef<ReturnType<typeof setTimeout>>(null);
|
const debounceTimerRef = useRef<ReturnType<typeof setTimeout>>(null);
|
||||||
const requestIdRef = useRef<number>(0);
|
const requestIdRef = useRef<number>(0);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () => setStableTexture(Texture.from(img));
|
||||||
|
img.src = picture.imageUrl;
|
||||||
|
}, [ picture ]);
|
||||||
|
|
||||||
const getColorMatrixEffects = useMemo(() => {
|
const getColorMatrixEffects = useMemo(() => {
|
||||||
return availableEffects.filter(effect => effect.colorMatrix);
|
return availableEffects.filter(effect => effect.colorMatrix);
|
||||||
}, [ availableEffects ]);
|
}, [ availableEffects ]);
|
||||||
@@ -108,12 +117,14 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
|
|||||||
setSelectedEffects([]);
|
setSelectedEffects([]);
|
||||||
return;
|
return;
|
||||||
case 'download': {
|
case 'download': {
|
||||||
(async () => {
|
if(!currentPictureUrl || !currentPictureUrl.startsWith('data:image/')) return;
|
||||||
const image = new Image();
|
|
||||||
image.src = currentPictureUrl;
|
const link = document.createElement('a');
|
||||||
const newWindow = window.open('');
|
link.href = currentPictureUrl;
|
||||||
newWindow.document.write(image.outerHTML);
|
link.download = 'camera_photo.png';
|
||||||
})();
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case 'zoom':
|
case 'zoom':
|
||||||
@@ -123,25 +134,29 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
|
|||||||
}, [ availableEffects, selectedEffectName, currentPictureUrl, getSelectedEffectIndex, onCancel, onCheckout, onClose ]);
|
}, [ availableEffects, selectedEffectName, currentPictureUrl, getSelectedEffectIndex, onCancel, onCheckout, onClose ]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if(!stableTexture) return;
|
||||||
|
|
||||||
const processThumbnails = async () => {
|
const processThumbnails = async () => {
|
||||||
const renderedEffects = await Promise.all(
|
const renderedEffects = await Promise.all(
|
||||||
availableEffects.map(effect =>
|
availableEffects.map(effect =>
|
||||||
GetRoomCameraWidgetManager().applyEffects(picture.texture, [ new RoomCameraWidgetSelectedEffect(effect, 1) ], false)
|
GetRoomCameraWidgetManager().applyEffects(stableTexture, [ new RoomCameraWidgetSelectedEffect(effect, 1) ], false)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
setEffectsThumbnails(renderedEffects.map((image, index) => new CameraPictureThumbnail(availableEffects[index].name, image.src)));
|
setEffectsThumbnails(renderedEffects.map((image, index) => new CameraPictureThumbnail(availableEffects[index].name, image.src)));
|
||||||
};
|
};
|
||||||
processThumbnails();
|
processThumbnails();
|
||||||
}, [ picture, availableEffects ]);
|
}, [ stableTexture, availableEffects ]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if(!stableTexture) return;
|
||||||
|
|
||||||
if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
|
if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
|
||||||
|
|
||||||
debounceTimerRef.current = setTimeout(() => {
|
debounceTimerRef.current = setTimeout(() => {
|
||||||
const id = ++requestIdRef.current;
|
const id = ++requestIdRef.current;
|
||||||
|
|
||||||
GetRoomCameraWidgetManager()
|
GetRoomCameraWidgetManager()
|
||||||
.applyEffects(picture.texture, selectedEffects, false)
|
.applyEffects(stableTexture, selectedEffects, false)
|
||||||
.then(imageElement => {
|
.then(imageElement => {
|
||||||
if (id !== requestIdRef.current) return;
|
if (id !== requestIdRef.current) return;
|
||||||
setCurrentPictureUrl(imageElement.src);
|
setCurrentPictureUrl(imageElement.src);
|
||||||
@@ -152,10 +167,10 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
|
|||||||
return () => {
|
return () => {
|
||||||
if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
|
if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
|
||||||
};
|
};
|
||||||
}, [ picture, selectedEffects ]);
|
}, [ stableTexture, selectedEffects ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroCardView className="w-[620px] h-[500px]">
|
<NitroCardView className="w-[600px] h-[500px]">
|
||||||
<NitroCardHeaderView headerText={ LocalizeText('camera.editor.button.text') } onCloseClick={ event => processAction('close') } />
|
<NitroCardHeaderView headerText={ LocalizeText('camera.editor.button.text') } onCloseClick={ event => processAction('close') } />
|
||||||
<NitroCardTabsView>
|
<NitroCardTabsView>
|
||||||
{ TABS.map(tab => (
|
{ TABS.map(tab => (
|
||||||
@@ -177,16 +192,14 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
|
|||||||
</Column>
|
</Column>
|
||||||
<Column justifyContent="between" overflow="hidden" size={ 7 }>
|
<Column justifyContent="between" overflow="hidden" size={ 7 }>
|
||||||
<Column center>
|
<Column center>
|
||||||
<LayoutImage
|
<div className="w-[325px] h-[325px] overflow-hidden">
|
||||||
style={{
|
{ currentPictureUrl && <img
|
||||||
width: '320px',
|
alt=""
|
||||||
height: '320px',
|
src={ currentPictureUrl }
|
||||||
backgroundImage: `url(${currentPictureUrl})`,
|
className="w-[325px] h-[325px] [image-rendering:pixelated]"
|
||||||
backgroundPosition: isZoomed ? 'center' : 'top left',
|
style={ isZoomed ? { transform: 'scale(2)', transformOrigin: 'center' } : undefined }
|
||||||
backgroundSize: isZoomed ? 'contain' : 'auto', // Zoom only affects display
|
/> }
|
||||||
backgroundRepeat: 'no-repeat'
|
</div>
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{ selectedEffectName && (
|
{ selectedEffectName && (
|
||||||
<Column center fullWidth gap={ 1 }>
|
<Column center fullWidth gap={ 1 }>
|
||||||
<Text>{ LocalizeText('camera.effect.name.' + selectedEffectName) }</Text>
|
<Text>{ LocalizeText('camera.effect.name.' + selectedEffectName) }</Text>
|
||||||
|
|||||||
Reference in New Issue
Block a user