🆙 Camera Fix

This commit is contained in:
duckietm
2026-03-18 09:17:04 +01:00
parent 2973c18ead
commit 9a6638219d
@@ -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 { Texture } from 'pixi.js';
import { FaSave, FaSearchMinus, FaSearchPlus, FaTrash } from 'react-icons/fa';
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';
export interface CameraWidgetEditorViewProps {
@@ -23,10 +24,18 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
const [ selectedEffects, setSelectedEffects ] = useState<IRoomCameraWidgetSelectedEffect[]>([]);
const [ effectsThumbnails, setEffectsThumbnails ] = useState<CameraPictureThumbnail[]>([]);
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 requestIdRef = useRef<number>(0);
useEffect(() =>
{
const img = new Image();
img.onload = () => setStableTexture(Texture.from(img));
img.src = picture.imageUrl;
}, [ picture ]);
const getColorMatrixEffects = useMemo(() => {
return availableEffects.filter(effect => effect.colorMatrix);
}, [ availableEffects ]);
@@ -108,12 +117,14 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
setSelectedEffects([]);
return;
case 'download': {
(async () => {
const image = new Image();
image.src = currentPictureUrl;
const newWindow = window.open('');
newWindow.document.write(image.outerHTML);
})();
if(!currentPictureUrl || !currentPictureUrl.startsWith('data:image/')) return;
const link = document.createElement('a');
link.href = currentPictureUrl;
link.download = 'camera_photo.png';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
return;
}
case 'zoom':
@@ -123,25 +134,29 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
}, [ availableEffects, selectedEffectName, currentPictureUrl, getSelectedEffectIndex, onCancel, onCheckout, onClose ]);
useEffect(() => {
if(!stableTexture) return;
const processThumbnails = async () => {
const renderedEffects = await Promise.all(
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)));
};
processThumbnails();
}, [ picture, availableEffects ]);
}, [ stableTexture, availableEffects ]);
useEffect(() => {
if(!stableTexture) return;
if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
debounceTimerRef.current = setTimeout(() => {
const id = ++requestIdRef.current;
GetRoomCameraWidgetManager()
.applyEffects(picture.texture, selectedEffects, false)
.applyEffects(stableTexture, selectedEffects, false)
.then(imageElement => {
if (id !== requestIdRef.current) return;
setCurrentPictureUrl(imageElement.src);
@@ -152,10 +167,10 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
return () => {
if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
};
}, [ picture, selectedEffects ]);
}, [ stableTexture, selectedEffects ]);
return (
<NitroCardView className="w-[620px] h-[500px]">
<NitroCardView className="w-[600px] h-[500px]">
<NitroCardHeaderView headerText={ LocalizeText('camera.editor.button.text') } onCloseClick={ event => processAction('close') } />
<NitroCardTabsView>
{ TABS.map(tab => (
@@ -177,16 +192,14 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
</Column>
<Column justifyContent="between" overflow="hidden" size={ 7 }>
<Column center>
<LayoutImage
style={{
width: '320px',
height: '320px',
backgroundImage: `url(${currentPictureUrl})`,
backgroundPosition: isZoomed ? 'center' : 'top left',
backgroundSize: isZoomed ? 'contain' : 'auto', // Zoom only affects display
backgroundRepeat: 'no-repeat'
}}
/>
<div className="w-[325px] h-[325px] overflow-hidden">
{ currentPictureUrl && <img
alt=""
src={ currentPictureUrl }
className="w-[325px] h-[325px] [image-rendering:pixelated]"
style={ isZoomed ? { transform: 'scale(2)', transformOrigin: 'center' } : undefined }
/> }
</div>
{ selectedEffectName && (
<Column center fullWidth gap={ 1 }>
<Text>{ LocalizeText('camera.effect.name.' + selectedEffectName) }</Text>