mirror of
https://github.com/motion-canvas/motion-canvas.git
synced 2026-01-11 14:57:56 -05:00
feat: added file type and quality options to rendering panel (#50)
The quality setting won't effect PNG, as I believe PNG files have to use for a quality setting. The setting input will then be inert when PNG, the default, is used. There are other design options available, but this is the simplest option that avoids frustration for the user. The image/webp option will not work on Safari, and there is no check for this occurance. The best option would be a proactive error message. Closes #24
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import {Scene, SceneDescription} from './scenes';
|
||||
import {Meta, Metadata} from './Meta';
|
||||
import {EventDispatcher, ValueDispatcher} from './events';
|
||||
import {Size, CanvasColorSpace} from './types';
|
||||
import {Size, CanvasColorSpace, CanvasOutputMimeType} from './types';
|
||||
import {AudioManager} from './media';
|
||||
import {ifHot} from './utils';
|
||||
|
||||
@@ -72,6 +72,14 @@ export class Project {
|
||||
this.updateCanvas();
|
||||
}
|
||||
|
||||
public set fileType(value: CanvasOutputMimeType) {
|
||||
this._fileType = value;
|
||||
}
|
||||
|
||||
public set quality(value: number) {
|
||||
this._quality = value;
|
||||
}
|
||||
|
||||
public set speed(value: number) {
|
||||
this._speed = value;
|
||||
this.reloadAll();
|
||||
@@ -119,6 +127,8 @@ export class Project {
|
||||
private readonly renderLookup: Record<number, Callback> = {};
|
||||
private _resolutionScale = 1;
|
||||
private _colorSpace: CanvasColorSpace = 'srgb';
|
||||
private _fileType: CanvasOutputMimeType = 'image/png';
|
||||
private _quality = 1;
|
||||
private _speed = 1;
|
||||
private framesPerSeconds = 30;
|
||||
private readonly sceneLookup: Record<string, Scene> = {};
|
||||
@@ -315,8 +325,8 @@ export class Project {
|
||||
};
|
||||
hot.send('motion-canvas:export', {
|
||||
frame,
|
||||
data: this.canvas.toDataURL('image/png'),
|
||||
mimeType: 'image/png',
|
||||
data: this.canvas.toDataURL(this._fileType, this._quality),
|
||||
mimeType: this._fileType,
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type {Project} from '../Project';
|
||||
import {EventDispatcher, ValueDispatcher} from '../events';
|
||||
import type {CanvasColorSpace} from '../types';
|
||||
import type {CanvasColorSpace, CanvasOutputMimeType} from '../types';
|
||||
|
||||
const MAX_AUDIO_DESYNC = 1 / 50;
|
||||
|
||||
@@ -18,6 +18,8 @@ export interface PlayerState extends Record<string, unknown> {
|
||||
fps: number;
|
||||
scale: number;
|
||||
colorSpace: CanvasColorSpace;
|
||||
fileType: CanvasOutputMimeType;
|
||||
quality: number;
|
||||
}
|
||||
|
||||
interface PlayerCommands {
|
||||
@@ -43,6 +45,8 @@ export class Player {
|
||||
fps: 30,
|
||||
scale: 1,
|
||||
colorSpace: 'srgb',
|
||||
fileType: 'image/png',
|
||||
quality: 1,
|
||||
});
|
||||
|
||||
public get onFrameChanged() {
|
||||
@@ -174,6 +178,16 @@ export class Player {
|
||||
this.project.render();
|
||||
}
|
||||
|
||||
public setFileType(fileType: CanvasOutputMimeType) {
|
||||
this.project.fileType = fileType;
|
||||
this.updateState({fileType});
|
||||
}
|
||||
|
||||
public setQuality(quality: number) {
|
||||
this.project.quality = quality;
|
||||
this.updateState({quality});
|
||||
}
|
||||
|
||||
public requestPreviousFrame(): void {
|
||||
this.commands.seek = this.frame.current - this.state.current.speed;
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export type CanvasColorSpace = 'srgb' | 'display-p3';
|
||||
export type CanvasOutputMimeType = 'image/png' | 'image/jpeg' | 'image/webp';
|
||||
|
||||
@@ -2,7 +2,10 @@ import {usePlayerState} from '../../hooks';
|
||||
import {Button, Group, Input, Label, Select} from '../controls';
|
||||
import {Pane} from '../tabs';
|
||||
import {usePlayer} from '../../contexts';
|
||||
import type {CanvasColorSpace} from '@motion-canvas/core/lib/types';
|
||||
import type {
|
||||
CanvasColorSpace,
|
||||
CanvasOutputMimeType,
|
||||
} from '@motion-canvas/core/lib/types';
|
||||
|
||||
export function Rendering() {
|
||||
const player = usePlayer();
|
||||
@@ -20,6 +23,12 @@ export function Rendering() {
|
||||
{value: 'display-p3', text: 'DCI-P3'},
|
||||
];
|
||||
|
||||
const fileTypes = [
|
||||
{value: 'image/png', text: 'png'},
|
||||
{value: 'image/jpeg', text: 'jpeg'},
|
||||
{value: 'image/webp', text: 'webp'},
|
||||
];
|
||||
|
||||
return (
|
||||
<Pane title="Rendering">
|
||||
<Group>
|
||||
@@ -94,6 +103,27 @@ export function Rendering() {
|
||||
onChange={value => player.setColorSpace(value as CanvasColorSpace)}
|
||||
/>
|
||||
</Group>
|
||||
<Group>
|
||||
<Label>File Type</Label>
|
||||
<Select
|
||||
options={fileTypes}
|
||||
value={state.fileType}
|
||||
onChange={value => player.setFileType(value as CanvasOutputMimeType)}
|
||||
/>
|
||||
</Group>
|
||||
<Group>
|
||||
<Label>Quality</Label>
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
max={1}
|
||||
value={state.quality}
|
||||
onChange={event => {
|
||||
const value = parseFloat((event.target as HTMLInputElement).value);
|
||||
player.setQuality(value);
|
||||
}}
|
||||
/>
|
||||
</Group>
|
||||
<Group>
|
||||
<Label />
|
||||
<Button main onClick={() => player.toggleRendering()}>
|
||||
|
||||
Reference in New Issue
Block a user