mirror of
https://github.com/sipeed/NanoKVM-USB.git
synced 2026-09-12 06:29:59 -05:00
add video scale (browser)
This commit is contained in:
@@ -10,7 +10,12 @@ import { Keyboard } from '@/components/keyboard';
|
||||
import { Menu } from '@/components/menu';
|
||||
import { Mouse } from '@/components/mouse';
|
||||
import { VirtualKeyboard } from '@/components/virtual-keyboard';
|
||||
import { resolutionAtom, serialStateAtom, videoStateAtom } from '@/jotai/device.ts';
|
||||
import {
|
||||
resolutionAtom,
|
||||
serialStateAtom,
|
||||
videoScaleAtom,
|
||||
videoStateAtom
|
||||
} from '@/jotai/device.ts';
|
||||
import { isKeyboardEnableAtom } from '@/jotai/keyboard.ts';
|
||||
import { mouseStyleAtom } from '@/jotai/mouse.ts';
|
||||
import { camera } from '@/libs/camera';
|
||||
@@ -23,6 +28,7 @@ const App = () => {
|
||||
const isBigScreen = useMediaQuery({ minWidth: 850 });
|
||||
|
||||
const mouseStyle = useAtomValue(mouseStyleAtom);
|
||||
const videoScale = useAtomValue(videoScaleAtom);
|
||||
const videoState = useAtomValue(videoStateAtom);
|
||||
const serialState = useAtomValue(serialStateAtom);
|
||||
const isKeyboardEnable = useAtomValue(isKeyboardEnableAtom);
|
||||
@@ -107,7 +113,13 @@ const App = () => {
|
||||
<video
|
||||
id="video"
|
||||
className={clsx('block min-h-[480px] min-w-[640px] select-none', mouseStyle)}
|
||||
style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'scale-down' }}
|
||||
style={{
|
||||
transform: `scale(${videoScale})`,
|
||||
transformOrigin: 'center',
|
||||
maxWidth: '100%',
|
||||
maxHeight: '100%',
|
||||
objectFit: 'scale-down'
|
||||
}}
|
||||
autoPlay
|
||||
playsInline
|
||||
/>
|
||||
|
||||
@@ -3,11 +3,13 @@ import { MonitorIcon } from 'lucide-react';
|
||||
|
||||
import { Device } from './device.tsx';
|
||||
import { Resolution } from './resolution.tsx';
|
||||
import { Scale } from './scale.tsx';
|
||||
|
||||
export const Video = () => {
|
||||
const content = (
|
||||
<div className="flex flex-col space-y-1">
|
||||
<Resolution />
|
||||
<Scale />
|
||||
<Device />
|
||||
</div>
|
||||
);
|
||||
|
||||
58
browser/src/components/menu/video/scale.tsx
Normal file
58
browser/src/components/menu/video/scale.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import { ReactElement, useEffect } from 'react'
|
||||
import { Popover, Slider } from 'antd'
|
||||
import { useAtom } from 'jotai'
|
||||
import { ScalingIcon } from 'lucide-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { videoScaleAtom } from '@/jotai/device.ts';
|
||||
import * as storage from '@/libs/storage';
|
||||
|
||||
export const Scale = (): ReactElement => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [videoScale, setVideoScale] = useAtom(videoScaleAtom)
|
||||
|
||||
useEffect(() => {
|
||||
const scale = storage.getVideoScale()
|
||||
if (scale) {
|
||||
setVideoScale(scale)
|
||||
}
|
||||
}, [])
|
||||
|
||||
async function updateScale(scale: number): Promise<void> {
|
||||
setVideoScale(scale)
|
||||
storage.setVideoScale(scale)
|
||||
}
|
||||
|
||||
const content = (
|
||||
<div className="h-[150px] w-[60px] py-3">
|
||||
<Slider
|
||||
vertical
|
||||
marks={{
|
||||
0.5: <span>x0.5</span>,
|
||||
1: <span>x1.0</span>,
|
||||
1.5: <span>x1.5</span>,
|
||||
2: <span>x2.0</span>
|
||||
}}
|
||||
range={false}
|
||||
included={false}
|
||||
min={0.5}
|
||||
max={2}
|
||||
step={0.1}
|
||||
defaultValue={videoScale}
|
||||
onChange={updateScale}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<Popover content={content} placement="rightTop" arrow={false} align={{ offset: [13, 0] }}>
|
||||
<div className="flex h-[30px] cursor-pointer items-center space-x-1 rounded px-3 text-neutral-300 hover:bg-neutral-700/50">
|
||||
<div className="flex h-[14px] w-[20px] items-end">
|
||||
<ScalingIcon size={16} />
|
||||
</div>
|
||||
<span>{t('video.scale')}</span>
|
||||
</div>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
@@ -24,6 +24,7 @@ const be = {
|
||||
},
|
||||
video: {
|
||||
resolution: 'Resolutie',
|
||||
scale: 'Schaal',
|
||||
customResolution: 'Aangepast',
|
||||
device: 'Toestel',
|
||||
custom: {
|
||||
@@ -62,5 +63,5 @@ const be = {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default be;
|
||||
|
||||
export default be;
|
||||
|
||||
@@ -24,6 +24,7 @@ const de = {
|
||||
},
|
||||
video: {
|
||||
resolution: 'Auflösung',
|
||||
scale: 'Skalierung',
|
||||
customResolution: 'Benutzerdefiniert',
|
||||
device: 'Gerät',
|
||||
custom: {
|
||||
@@ -62,5 +63,5 @@ const de = {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default de;
|
||||
|
||||
export default de;
|
||||
|
||||
@@ -24,6 +24,7 @@ const en = {
|
||||
},
|
||||
video: {
|
||||
resolution: 'Resolution',
|
||||
scale: 'Scale',
|
||||
customResolution: 'Custom',
|
||||
device: 'Device',
|
||||
custom: {
|
||||
|
||||
@@ -24,6 +24,7 @@ const ko = {
|
||||
},
|
||||
video: {
|
||||
resolution: '해상도',
|
||||
scale: '배율',
|
||||
customResolution: '사용자 정의',
|
||||
device: '장치',
|
||||
custom: {
|
||||
|
||||
@@ -24,6 +24,7 @@ const nl = {
|
||||
},
|
||||
video: {
|
||||
resolution: 'Resolutie',
|
||||
scale: 'Schaal',
|
||||
customResolution: 'Aangepast',
|
||||
device: 'Apparaat',
|
||||
custom: {
|
||||
@@ -62,5 +63,5 @@ const nl = {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default nl;
|
||||
|
||||
export default nl;
|
||||
|
||||
@@ -24,6 +24,7 @@ const ru = {
|
||||
},
|
||||
video: {
|
||||
resolution: 'Разрешение',
|
||||
scale: 'Масштаб',
|
||||
customResolution: 'Пользовательское',
|
||||
device: 'Видеоустройство',
|
||||
custom: {
|
||||
|
||||
@@ -22,6 +22,7 @@ const zh = {
|
||||
},
|
||||
video: {
|
||||
resolution: '分辨率',
|
||||
scale: '缩放',
|
||||
customResolution: '自定义',
|
||||
device: '设备',
|
||||
custom: {
|
||||
|
||||
@@ -10,6 +10,8 @@ export const resolutionAtom = atom<Resolution>({
|
||||
height: 1080
|
||||
});
|
||||
|
||||
export const videoScaleAtom = atom<number>(1.0)
|
||||
|
||||
export const videoDeviceIdAtom = atom('');
|
||||
export const videoStateAtom = atom<VideoState>('disconnected');
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ const LANGUAGE_KEY = 'nanokvm-usb-language';
|
||||
const VIDEO_DEVICE_ID_KEY = 'nanokvm-usb-video-device-id';
|
||||
const VIDEO_RESOLUTION_KEY = 'nanokvm-usb-video-resolution';
|
||||
const CUSTOM_RESOLUTION_KEY = 'nanokvm-usb-custom-resolution';
|
||||
const VIDEO_SCALE_KEY = 'nanokvm-usb-video-scale'
|
||||
const IS_MENU_OPEN_KEY = 'nanokvm-is-menu-open';
|
||||
const MOUSE_STYLE_KEY = 'nanokvm-usb-mouse-style';
|
||||
const MOUSE_MODE_KEY = 'nanokvm-usb-mouse-mode';
|
||||
@@ -56,6 +57,18 @@ export function removeCustomResolutions() {
|
||||
localStorage.removeItem(CUSTOM_RESOLUTION_KEY);
|
||||
}
|
||||
|
||||
export function getVideoScale(): number | null {
|
||||
const scale = localStorage.getItem(VIDEO_SCALE_KEY)
|
||||
if (scale && Number(scale)) {
|
||||
return Number(scale)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export function setVideoScale(scale: number): void {
|
||||
localStorage.setItem(VIDEO_SCALE_KEY, String(scale))
|
||||
}
|
||||
|
||||
export function getIsMenuOpen(): boolean {
|
||||
const state = localStorage.getItem(IS_MENU_OPEN_KEY);
|
||||
if (!state) {
|
||||
|
||||
Reference in New Issue
Block a user