mirror of
https://github.com/motion-canvas/motion-canvas.git
synced 2026-01-12 07:18:01 -05:00
feat: general improvements
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
"author": "aarthificial",
|
||||
"license": "ISC",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"build": "webpack",
|
||||
"watch": "tsc -w"
|
||||
},
|
||||
"types": "dist/global.d.ts",
|
||||
|
||||
14
src/Scene.ts
14
src/Scene.ts
@@ -30,6 +30,7 @@ export interface TimeEvent {
|
||||
name: string;
|
||||
initialFrame: number;
|
||||
offset: number;
|
||||
fps: number;
|
||||
}
|
||||
|
||||
export class Scene extends Layer {
|
||||
@@ -80,7 +81,16 @@ export class Scene extends Layer {
|
||||
this.storageKey = `scene-${this.name()}`;
|
||||
const storedEvents = localStorage.getItem(this.storageKey);
|
||||
if (storedEvents) {
|
||||
this.storedEventLookup = JSON.parse(storedEvents);
|
||||
const fps = project.framesPerSeconds;
|
||||
for (const event of Object.values<TimeEvent>(JSON.parse(storedEvents))) {
|
||||
const oldFps = event.fps ?? 30;
|
||||
if (oldFps !== fps) {
|
||||
event.initialFrame = (event.offset * fps) / oldFps;
|
||||
event.offset = (event.offset * fps) / oldFps;
|
||||
}
|
||||
event.fps = fps;
|
||||
this.storedEventLookup[event.name] = event;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +105,7 @@ export class Scene extends Layer {
|
||||
...this.timeEventLookup,
|
||||
};
|
||||
this.timeEventLookup = {};
|
||||
this.timeEventsChanged.dispatch([]);
|
||||
}
|
||||
|
||||
public async reset(previousScene: Scene = null) {
|
||||
@@ -191,6 +202,7 @@ export class Scene extends Layer {
|
||||
name,
|
||||
initialFrame,
|
||||
offset: this.storedEventLookup[name]?.offset ?? 0,
|
||||
fps: this.project.framesPerSeconds,
|
||||
};
|
||||
this.timeEventsChanged.dispatch(this.timeEvents);
|
||||
} else if (this.timeEventLookup[name].initialFrame !== initialFrame) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
easeInOutCubic,
|
||||
easeOutExpo,
|
||||
linear,
|
||||
map,
|
||||
spacingTween,
|
||||
tween,
|
||||
} from '../tweening';
|
||||
@@ -68,6 +69,7 @@ export function showSurface(surface: Surface): ThreadGenerator {
|
||||
decorate(showCircle, threadable());
|
||||
export function showCircle(
|
||||
surface: Surface,
|
||||
duration: number = 0.6,
|
||||
origin?: Origin | Vector2d,
|
||||
): ThreadGenerator {
|
||||
const position =
|
||||
@@ -85,9 +87,24 @@ export function showCircle(
|
||||
mask.radius = 0;
|
||||
|
||||
return chain(
|
||||
tween(target / 2000, value => {
|
||||
tween(duration, value => {
|
||||
mask.radius = easeInOutCubic(value, 0, target);
|
||||
}),
|
||||
() => surface.setCircleMask(null),
|
||||
);
|
||||
}
|
||||
|
||||
export function unravelSurface(surface: Surface): ThreadGenerator {
|
||||
const mask = surface.getMask();
|
||||
surface.setMask({...mask, height: 0});
|
||||
return tween(
|
||||
0.5,
|
||||
value => {
|
||||
surface.setMask({
|
||||
...mask,
|
||||
height: map(0, mask.height, easeInOutCubic(value)),
|
||||
});
|
||||
},
|
||||
() => surface.setMask(null),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -28,13 +28,17 @@ export interface SurfaceTransitionConfig {
|
||||
}
|
||||
|
||||
decorate(surfaceTransition, threadable());
|
||||
export function surfaceTransition(fromSurfaceOriginal: Surface) {
|
||||
const fromSurface = fromSurfaceOriginal
|
||||
.clone()
|
||||
.moveTo(fromSurfaceOriginal.parent)
|
||||
.zIndex(fromSurfaceOriginal.zIndex());
|
||||
export function surfaceTransition(fromSurfaceOriginal: Surface, clone = true) {
|
||||
const fromSurface = clone
|
||||
? fromSurfaceOriginal
|
||||
.clone()
|
||||
.moveTo(fromSurfaceOriginal.parent)
|
||||
.zIndex(fromSurfaceOriginal.zIndex())
|
||||
: fromSurfaceOriginal;
|
||||
|
||||
fromSurfaceOriginal.hide();
|
||||
if (clone) {
|
||||
fromSurfaceOriginal.hide();
|
||||
}
|
||||
const from = fromSurfaceOriginal.getMask();
|
||||
|
||||
decorate(surfaceTransitionExecutor, threadable());
|
||||
|
||||
@@ -39,6 +39,8 @@ export class ColorPicker extends Surface {
|
||||
public readonly b: Range;
|
||||
public readonly a: Range;
|
||||
|
||||
public parsedColor: ReturnType<typeof parseColor>;
|
||||
|
||||
public constructor(config?: ColorPickerConfig) {
|
||||
super(config);
|
||||
|
||||
@@ -73,6 +75,8 @@ export class ColorPicker extends Surface {
|
||||
}
|
||||
|
||||
private updateColor() {
|
||||
if (!this.a) return;
|
||||
|
||||
const style = this.style();
|
||||
const preview = this.previewColor();
|
||||
|
||||
@@ -80,13 +84,13 @@ export class ColorPicker extends Surface {
|
||||
...style,
|
||||
foreground: preview,
|
||||
});
|
||||
const color = parseColor(preview);
|
||||
color.a = Math.round(color.a * 255);
|
||||
this.parsedColor = parseColor(preview);
|
||||
this.parsedColor.a = Math.round(this.parsedColor.a * 255);
|
||||
|
||||
this.r.value(color.r);
|
||||
this.g.value(color.g);
|
||||
this.b.value(color.b);
|
||||
this.a.value(color.a);
|
||||
this.r.value(this.parsedColor.r);
|
||||
this.g.value(this.parsedColor.g);
|
||||
this.b.value(this.parsedColor.b);
|
||||
this.a.value(this.parsedColor.a);
|
||||
this.preview.fill(preview);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ import {map} from '../tweening';
|
||||
export interface ConnectionConfig extends ContainerConfig {
|
||||
start?: Pin;
|
||||
end?: Pin;
|
||||
startTarget?: Node;
|
||||
endTarget?: Node;
|
||||
crossing?: Node;
|
||||
arrow?: Arrow;
|
||||
}
|
||||
@@ -59,6 +61,13 @@ export class Connection extends Group {
|
||||
if (!this.arrow.getParent()) {
|
||||
this.add(this.arrow);
|
||||
}
|
||||
|
||||
if (config?.startTarget) {
|
||||
this.start.target(config?.startTarget);
|
||||
}
|
||||
if (config?.endTarget) {
|
||||
this.end.target(config?.endTarget);
|
||||
}
|
||||
}
|
||||
|
||||
private static measurePosition(
|
||||
|
||||
@@ -2,21 +2,26 @@ import {Context} from 'konva/lib/Context';
|
||||
import {GetSet} from 'konva/lib/types';
|
||||
import {LayoutShape, LayoutShapeConfig} from './LayoutShape';
|
||||
import {KonvaNode, getset} from '../decorators';
|
||||
import {CanvasHelper} from '../helpers';
|
||||
|
||||
export interface GridConfig extends LayoutShapeConfig {
|
||||
gridSize?: number;
|
||||
subdivision?: boolean;
|
||||
checker?: boolean;
|
||||
}
|
||||
|
||||
@KonvaNode()
|
||||
export class Grid extends LayoutShape {
|
||||
@getset(16, Grid.prototype.recalculate)
|
||||
public gridSize: GetSet<number, this>;
|
||||
@getset(false)
|
||||
public checker: GetSet<GridConfig['checker'], this>;
|
||||
|
||||
@getset(false)
|
||||
public subdivision: GetSet<GridConfig['subdivision'], this>;
|
||||
|
||||
private path: Path2D;
|
||||
private checkerPath: Path2D;
|
||||
|
||||
public constructor(config?: GridConfig) {
|
||||
super(config);
|
||||
@@ -42,10 +47,26 @@ export class Grid extends LayoutShape {
|
||||
context.stroke(this.path);
|
||||
}
|
||||
};
|
||||
this._fillFunc = context => {
|
||||
if (!this.checkerPath) this.recalculate();
|
||||
const size = this.size();
|
||||
context._context.clip(
|
||||
CanvasHelper.roundRectPath(
|
||||
new Path2D(),
|
||||
size.width / -2,
|
||||
size.height / -2,
|
||||
size.width,
|
||||
size.height,
|
||||
8,
|
||||
),
|
||||
);
|
||||
context.fill(this.checkerPath);
|
||||
};
|
||||
}
|
||||
|
||||
private recalculate() {
|
||||
this.path = new Path2D();
|
||||
this.checkerPath = new Path2D();
|
||||
|
||||
let gridSize = this.gridSize();
|
||||
if (gridSize < 1) {
|
||||
@@ -56,9 +77,20 @@ export class Grid extends LayoutShape {
|
||||
size.width = size.width / 2 + gridSize;
|
||||
size.height = size.height / 2 + gridSize;
|
||||
|
||||
let i = 0;
|
||||
for (let x = -size.width; x <= size.width; x += gridSize) {
|
||||
this.path.moveTo(x, -size.height);
|
||||
this.path.lineTo(x, size.height);
|
||||
|
||||
for (
|
||||
let y = -size.height + (i % 2 ? 0 : gridSize);
|
||||
y <= size.height;
|
||||
y += gridSize * 2
|
||||
) {
|
||||
this.checkerPath.rect(x, y, gridSize, gridSize);
|
||||
this.checkerPath.rect(x, y, gridSize, gridSize);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
for (let y = -size.height; y <= size.height; y += gridSize) {
|
||||
@@ -68,6 +100,10 @@ export class Grid extends LayoutShape {
|
||||
}
|
||||
|
||||
public _sceneFunc(context: Context) {
|
||||
context.strokeShape(this);
|
||||
if (this.checker()) {
|
||||
context.fillShape(this);
|
||||
} else {
|
||||
context.strokeShape(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,11 @@ import {getset, KonvaNode, threadable} from '../decorators';
|
||||
import {GeneratorHelper} from '../helpers';
|
||||
import {InterpolationFunction, map, tween} from '../tweening';
|
||||
import {cancel, ThreadGenerator} from '../threading';
|
||||
import {parseColor} from 'mix-color';
|
||||
|
||||
export interface SpriteData {
|
||||
fileName: string;
|
||||
src: string;
|
||||
data: number[];
|
||||
width: number;
|
||||
height: number;
|
||||
@@ -49,6 +51,7 @@ export class Sprite extends LayoutShape {
|
||||
private spriteData: SpriteData = {
|
||||
height: 0,
|
||||
width: 0,
|
||||
src: '',
|
||||
data: [],
|
||||
fileName: '',
|
||||
};
|
||||
@@ -120,21 +123,26 @@ export class Sprite extends LayoutShape {
|
||||
(skin.data[skinId + 3] / 255) *
|
||||
255,
|
||||
);
|
||||
|
||||
if (mask || this.baseMask) {
|
||||
const maskValue = map(
|
||||
mask?.data[id] ?? 255,
|
||||
this.baseMask?.data[id] ?? 255,
|
||||
this.baseMaskBlend,
|
||||
);
|
||||
this.imageData.data[id + 3] *= map(1, maskValue / 255, blend);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.imageData.data.set(this.spriteData.data);
|
||||
}
|
||||
|
||||
if (mask || this.baseMask) {
|
||||
for (let y = 0; y < this.spriteData.height; y++) {
|
||||
for (let x = 0; x < this.spriteData.width; x++) {
|
||||
const id = this.positionToId({x, y});
|
||||
const maskValue = map(
|
||||
mask?.data[id] ?? 255,
|
||||
this.baseMask?.data[id] ?? 255,
|
||||
this.baseMaskBlend,
|
||||
);
|
||||
this.imageData.data[id + 3] *= map(1, maskValue / 255, blend);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.context.putImageData(this.imageData, 0, 0);
|
||||
this.fire(SPRITE_CHANGE_EVENT);
|
||||
this.fireLayoutChange();
|
||||
@@ -156,6 +164,20 @@ export class Sprite extends LayoutShape {
|
||||
return this.task;
|
||||
}
|
||||
|
||||
@threadable()
|
||||
public *playOnce(
|
||||
animation: SpriteData[],
|
||||
next: SpriteData[] = null,
|
||||
): ThreadGenerator {
|
||||
next ??= this.animation();
|
||||
this.animation(animation);
|
||||
for (let i = 0; i < animation.length; i++) {
|
||||
this.frame(i);
|
||||
yield* waitFor(1 / this.fps());
|
||||
}
|
||||
this.animation(next);
|
||||
}
|
||||
|
||||
@threadable()
|
||||
public *stop() {
|
||||
if (this.task) {
|
||||
@@ -164,12 +186,17 @@ export class Sprite extends LayoutShape {
|
||||
}
|
||||
}
|
||||
|
||||
private synced = false;
|
||||
|
||||
@threadable()
|
||||
private *playRunner(): ThreadGenerator {
|
||||
this.frame(0);
|
||||
while (this.task !== null) {
|
||||
if (this.playing()) {
|
||||
this.synced = true;
|
||||
this.frame(this.frame() + 1);
|
||||
} else {
|
||||
this.synced = false;
|
||||
}
|
||||
yield* waitFor(1 / this.fps());
|
||||
}
|
||||
@@ -178,7 +205,11 @@ export class Sprite extends LayoutShape {
|
||||
@threadable()
|
||||
public *waitForFrame(frame: number): ThreadGenerator {
|
||||
let limit = 1000;
|
||||
while (this.frame() !== frame && limit > 0) {
|
||||
while (
|
||||
this.frame() % this.animation().length !== frame &&
|
||||
limit > 0 &&
|
||||
!this.synced
|
||||
) {
|
||||
limit--;
|
||||
yield;
|
||||
}
|
||||
@@ -218,6 +249,16 @@ export class Sprite extends LayoutShape {
|
||||
.padStart(3, ' ')}, ${this.imageData.data[id + 3] / 255})`;
|
||||
}
|
||||
|
||||
public getParsedColorAt(position: Vector2d): ReturnType<typeof parseColor> {
|
||||
const id = this.positionToId(position);
|
||||
return {
|
||||
r: this.imageData.data[id],
|
||||
g: this.imageData.data[id + 1],
|
||||
b: this.imageData.data[id + 2],
|
||||
a: this.imageData.data[id + 3],
|
||||
}
|
||||
}
|
||||
|
||||
public positionToId(position: Vector2d): number {
|
||||
return (position.y * this.imageData.width + position.x) * 4;
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ export class ThreeView extends LayoutShape {
|
||||
private handleCanvasSizeChange() {
|
||||
if (!this.renderer) return;
|
||||
|
||||
const size = this.canvasSize();
|
||||
const size = {...this.canvasSize()};
|
||||
const camera = this.camera();
|
||||
|
||||
const ratio = size.width / size.height;
|
||||
@@ -117,6 +117,7 @@ export class ThreeView extends LayoutShape {
|
||||
size.width *= this.quality();
|
||||
size.height *= this.quality();
|
||||
this.renderer.setSize(size.width, size.height);
|
||||
this.fireLayoutChange();
|
||||
}
|
||||
|
||||
getLayoutSize(): Size {
|
||||
@@ -125,7 +126,7 @@ export class ThreeView extends LayoutShape {
|
||||
|
||||
_sceneFunc(context: Context) {
|
||||
const scale = this.quality();
|
||||
const size = this.canvasSize();
|
||||
const size = {...this.canvasSize()};
|
||||
|
||||
if (this.renderedFrames < 1) {
|
||||
this.renderedFrames = this.skipFrames();
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import {waitFor} from '../animations';
|
||||
import {decorate, threadable} from '../decorators';
|
||||
import {ThreadGenerator} from '../threading';
|
||||
import {isThreadGenerator, ThreadGenerator} from '../threading';
|
||||
|
||||
decorate(delay, threadable());
|
||||
export function* delay(time: number, task: ThreadGenerator): ThreadGenerator {
|
||||
export function* delay(
|
||||
time: number,
|
||||
task: ThreadGenerator | Function,
|
||||
): ThreadGenerator {
|
||||
yield* waitFor(time);
|
||||
yield* task;
|
||||
if (isThreadGenerator(task)) {
|
||||
yield* task;
|
||||
} else {
|
||||
task();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,3 +3,4 @@ export * from './any';
|
||||
export * from './chain';
|
||||
export * from './delay';
|
||||
export * from './loop';
|
||||
export * from './every';
|
||||
|
||||
@@ -4,10 +4,10 @@ import {ThreadGenerator} from '../threading';
|
||||
decorate(loop, threadable());
|
||||
export function* loop(
|
||||
iterations: number,
|
||||
factory: () => ThreadGenerator | void,
|
||||
factory: (i: number) => ThreadGenerator | void,
|
||||
) {
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
const generator = factory();
|
||||
const generator = factory(i);
|
||||
if (generator) {
|
||||
yield* generator;
|
||||
} else {
|
||||
|
||||
@@ -242,6 +242,9 @@ export class Player {
|
||||
this.syncAudio(-3);
|
||||
this.audioError = false;
|
||||
} catch (e) {
|
||||
if (!this.audioError) {
|
||||
console.error(e);
|
||||
}
|
||||
this.audioError = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ void main() {
|
||||
|
||||
gl_FragColor = mix(
|
||||
gl_FragColor,
|
||||
mix(vec4(0.0, 0.0, 0.0, 1.0), highlightDiffuse, vHighlight),
|
||||
mix(vec4(0.0, 0.0, 0.0, gl_FragColor.a), vec4(highlightDiffuse.rgb, gl_FragColor.a), vHighlight),
|
||||
edge
|
||||
);
|
||||
#else
|
||||
|
||||
@@ -46,5 +46,5 @@ export function getFontColor(background: string) {
|
||||
const brightness = Math.round(
|
||||
(color.r * 299 + color.g * 587 + color.b * 114) / 1000,
|
||||
);
|
||||
return brightness > 125 ? 'rgba(0, 0, 0, 0.87)' : 'rgba(0, 0, 0, 0.6)';
|
||||
return brightness > 125 ? 'rgba(0, 0, 0, 0.87)' : 'rgba(255, 255, 255, 0.87)';
|
||||
}
|
||||
|
||||
@@ -87,6 +87,14 @@ export class Animator<Type, This extends Node> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public do(callback: Function): this {
|
||||
this.keys.push(function* (): ThreadGenerator {
|
||||
callback();
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public diff<Rest extends any[]>(
|
||||
value: Type,
|
||||
time: number,
|
||||
@@ -124,7 +132,7 @@ export class Animator<Type, This extends Node> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public waitUntil(time: number): this {
|
||||
public waitUntil(time: number | string): this {
|
||||
this.keys.push(() => waitUntil(time));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,16 @@ export function easeInOutCubic(value: number, from = 0, to = 1) {
|
||||
return map(from, to, value);
|
||||
}
|
||||
|
||||
export function easeOutCubic(value: number, from = 0, to = 1): number {
|
||||
value = 1 - Math.pow(1 - value, 3);
|
||||
return map(from, to, value);
|
||||
}
|
||||
|
||||
export function easeInCubic(value: number, from = 0, to = 1): number {
|
||||
value = value * value * value;
|
||||
return map(from, to, value);
|
||||
}
|
||||
|
||||
export function easeInOutQuint(value: number, from = 0, to = 1) {
|
||||
value =
|
||||
value < 0.5
|
||||
|
||||
@@ -115,7 +115,7 @@ export function calculateRatio(
|
||||
ratio /= numberOfValues;
|
||||
}
|
||||
|
||||
return ratio;
|
||||
return isNaN(ratio) ? 1 : ratio;
|
||||
}
|
||||
|
||||
export function map(from: number, to: number, value: number) {
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
const path = require('path');
|
||||
const {readdirSync} = require('fs');
|
||||
const loadImage = require('../utils/load-image');
|
||||
const nameRegex = /([^\d]*)\d+\.png$/;
|
||||
const nameRegex = /[^\d]*(\d+)\.png$/;
|
||||
|
||||
function loader () {
|
||||
function loader() {
|
||||
const callback = this.async();
|
||||
const directoryPath = path.dirname(this.resourcePath);
|
||||
|
||||
const files = readdirSync(directoryPath)
|
||||
.filter(file => nameRegex.test(file))
|
||||
.map(file => path.resolve(directoryPath, file));
|
||||
.map(file => nameRegex.exec(file))
|
||||
.filter(match => !!match)
|
||||
.map(match => [match.input, parseInt(match[1])])
|
||||
.sort(([, indexA], [, indexB]) =>
|
||||
indexA < indexB ? -1 : indexA > indexB ? 1 : 0,
|
||||
)
|
||||
.map(([file]) => path.resolve(directoryPath, file));
|
||||
|
||||
files.forEach(file => this.addDependency(file));
|
||||
|
||||
|
||||
@@ -7,12 +7,17 @@ import WebpackDevServer from 'webpack-dev-server';
|
||||
|
||||
const projectFile = path.resolve(process.cwd(), process.argv[2]);
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const withUI = process.argv[3] === '--ui';
|
||||
|
||||
const compiler = webpack({
|
||||
entry: {
|
||||
index: projectFile,
|
||||
ui: path.resolve(__dirname, '../../ui/src/index.ts'),
|
||||
},
|
||||
entry: withUI
|
||||
? {
|
||||
index: projectFile,
|
||||
ui: path.resolve(__dirname, '../../ui/src/index.ts'),
|
||||
}
|
||||
: {
|
||||
index: projectFile,
|
||||
},
|
||||
mode: 'development',
|
||||
devtool: 'inline-source-map',
|
||||
module: {
|
||||
@@ -95,6 +100,7 @@ const compiler = webpack({
|
||||
output: {
|
||||
filename: `[name].js`,
|
||||
path: __dirname,
|
||||
uniqueName: 'motion-canvas',
|
||||
},
|
||||
experiments: {
|
||||
topLevelAwait: true,
|
||||
|
||||
@@ -22,6 +22,7 @@ module.exports = async function (fileName) {
|
||||
|
||||
return {
|
||||
fileName,
|
||||
src: image.src,
|
||||
data: Array.from(imageData.data),
|
||||
width: dimensions.width,
|
||||
height: dimensions.height,
|
||||
|
||||
52
webpack.config.js
Normal file
52
webpack.config.js
Normal file
@@ -0,0 +1,52 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
ui: path.resolve(__dirname, '../ui/src/index.ts'),
|
||||
},
|
||||
mode: 'production',
|
||||
devtool: false,
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.svg$/,
|
||||
type: 'asset',
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
{loader: 'style-loader'},
|
||||
{loader: 'css-loader', options: {modules: true}},
|
||||
{loader: 'sass-loader'},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
// include: path.resolve(__dirname, '../ui/'),
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
configFile: path.resolve(__dirname, '../ui/tsconfig.json'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.ts', '.tsx'],
|
||||
alias: {
|
||||
'@motion-canvas/core': path.resolve(__dirname, 'src'),
|
||||
},
|
||||
},
|
||||
optimization: {
|
||||
runtimeChunk: {
|
||||
name: 'runtime',
|
||||
},
|
||||
},
|
||||
output: {
|
||||
filename: `[name].js`,
|
||||
path: path.resolve(__dirname, 'public'),
|
||||
uniqueName: 'motion-canvas',
|
||||
},
|
||||
experiments: {
|
||||
topLevelAwait: true,
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user