diff --git a/.gitignore b/.gitignore
index b7b7533b..485396e5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,7 @@ types
out.png
cmj
.test-temp
+.history
# Numerous always-ignore extensions
*.diff
diff --git a/README.md b/README.md
index 1ed618e1..19bb7ea8 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
+
Konva
diff --git a/package.json b/package.json
index 4ca58eae..8e854f41 100644
--- a/package.json
+++ b/package.json
@@ -44,13 +44,13 @@
"test": "npm run test:browser && npm run test:node && npm run test:import",
"test:build": "PARCEL_WORKER_BACKEND=process parcel build ./test/unit-tests.html --dist-dir ./test-build --target none --public-url ./ --no-source-maps",
"test:browser": "npm run test:build && mocha-headless-chrome -f ./test-build/unit-tests.html -a disable-web-security -a no-sandbox -a disable-setuid-sandbox",
- "test:watch": "rm -rf ./.parcel-cache && PARCEL_WORKERS=0 parcel serve ./test/unit-tests.html ./test/manual-tests.html ./test/sandbox.html ./test/text-paths.html ./test/bunnies.html",
+ "test:watch": "rimraf ./.parcel-cache && PARCEL_WORKERS=0 parcel serve ./test/unit-tests.html ./test/manual-tests.html ./test/sandbox.html ./test/text-paths.html ./test/bunnies.html",
"test:node:canvas": "mocha -r tsx -r ./test/node-canvas-global-setup.mjs --extension ts --recursive './test/unit/' --exit",
"test:node:skia": "mocha -r tsx -r ./test/node-skia-global-setup.mjs --extension ts --recursive './test/unit/' --exit",
"test:node": "npm run test:node:canvas && npm run test:node:skia",
"tsc": "tsc --removeComments",
"rollup": "rollup -c",
- "clean": "rm -rf ./lib && rm -rf ./types && rm -rf ./cmj && rm -rf ./test-build",
+ "clean": "rimraf ./lib && rimraf ./types && rimraf ./cmj && rimraf ./test-build",
"watch": "rollup -c -w",
"size": "size-limit"
},
@@ -107,6 +107,7 @@
"parcel": "2.15.4",
"prettier": "^3.6.2",
"process": "^0.11.10",
+ "rimraf": "^6.0.1",
"rollup": "^4.48.0",
"rollup-plugin-typescript2": "^0.36.0",
"size-limit": "^11.2.0",
diff --git a/src/Container.ts b/src/Container.ts
index 83d42e8b..36d257d9 100644
--- a/src/Container.ts
+++ b/src/Container.ts
@@ -32,7 +32,8 @@ export interface ContainerConfig extends NodeConfig {
*/
export abstract class Container<
ChildType extends Node = Node,
-> extends Node {
+ Config extends ContainerConfig = ContainerConfig
+> extends Node {
children: Array = [];
/**
diff --git a/src/Global.ts b/src/Global.ts
index 367e76d1..093eb9de 100644
--- a/src/Global.ts
+++ b/src/Global.ts
@@ -196,7 +196,7 @@ export const Konva = {
_injectGlobal(Konva) {
if (typeof glob.Konva !== 'undefined') {
console.error(
- 'Severa Konva instances detected. It is not recommended to use multiple Konva instances in the same environment.'
+ 'Several Konva instances detected. It is not recommended to use multiple Konva instances in the same environment.'
);
}
glob.Konva = Konva;
diff --git a/src/Node.ts b/src/Node.ts
index 25f48cf5..b9103a1e 100644
--- a/src/Node.ts
+++ b/src/Node.ts
@@ -10,7 +10,7 @@ import type { Layer } from './Layer.ts';
import type { Shape } from './Shape.ts';
import type { Stage } from './Stage.ts';
import type { GetSet, IRect, Vector2d } from './types.ts';
-import { Transform, Util } from './Util.ts';
+import { Transform, Util, type AnyString } from './Util.ts';
import {
getBooleanValidator,
getNumberValidator,
@@ -134,9 +134,8 @@ type globalCompositeOperationType =
| 'color'
| 'luminosity';
-export interface NodeConfig {
- // allow any custom attribute
- [index: string]: any;
+// allow any custom attribute
+export type NodeConfig = {}> = Props & {
x?: number;
y?: number;
width?: number;
@@ -223,6 +222,20 @@ export type KonvaEventListener = (
ev: KonvaEventObject
) => void;
+export type CanvasConfig = {
+ x?: number;
+ y?: number;
+ width?: number;
+ height?: number;
+ pixelRatio?: number;
+ imageSmoothingEnabled?: boolean;
+};
+
+export type ImageConfig = CanvasConfig & {
+ mimeType?: string;
+ quality?: number;
+};
+
/**
* Node constructor. Nodes are entities that can be transformed, layered,
* and have bound events. The stage, layers, groups, and shapes all extend Node.
@@ -393,17 +406,13 @@ export abstract class Node {
* drawBorder: true
* });
*/
- cache(config?: {
- x?: number;
- y?: number;
- width?: number;
- height?: number;
- drawBorder?: boolean;
- offset?: number;
- pixelRatio?: number;
- imageSmoothingEnabled?: boolean;
- hitCanvasPixelRatio?: number;
- }) {
+ cache(
+ config?: CanvasConfig & {
+ drawBorder?: boolean;
+ offset?: number;
+ hitCanvasPixelRatio?: number;
+ }
+ ) {
const conf = config || {};
let rect = {} as IRect;
@@ -1015,13 +1024,13 @@ export abstract class Node {
* @example
* var x = node.getAttr('x');
*/
- getAttr(attr: string) {
- const method = 'get' + Util._capitalize(attr);
+ getAttr>(attr: K): K extends keyof AttrConfig ? AttrConfig[K] : any {
+ const method = 'get' + Util._capitalize(attr as string);
if (Util._isFunction((this as any)[method])) {
return (this as any)[method]();
}
// otherwise get directly
- return this.attrs[attr] as T | undefined;
+ return this.attrs[attr];
}
/**
* get ancestors
@@ -1050,8 +1059,8 @@ export abstract class Node {
* @name Konva.Node#getAttrs
* @returns {Object}
*/
- getAttrs() {
- return (this.attrs || {}) as Config & Record;
+ getAttrs(): Config {
+ return (this.attrs || {});
}
/**
* set multiple attrs at once using an object literal
@@ -1065,7 +1074,7 @@ export abstract class Node {
* fill: 'red'
* });
*/
- setAttrs(config: any) {
+ setAttrs(config?: Config) {
this._batchTransformChanges(() => {
let key, method;
if (!config) {
@@ -1624,7 +1633,7 @@ export abstract class Node {
* @returns {Object}
*/
toObject() {
- let attrs = this.getAttrs() as any,
+ let attrs = this.getAttrs(),
key,
val,
getter,
@@ -2111,7 +2120,7 @@ export abstract class Node {
* @example
* var canvas = node.toCanvas();
*/
- toCanvas(config?) {
+ toCanvas(config?: CanvasConfig) {
return this._toKonvaCanvas(config)._canvas;
}
/**
@@ -2137,16 +2146,11 @@ export abstract class Node {
* @param {Boolean} [config.imageSmoothingEnabled] set this to false if you want to disable imageSmoothing
* @returns {String}
*/
- toDataURL(config?: {
- x?: number;
- y?: number;
- width?: number;
- height?: number;
- pixelRatio?: number;
- mimeType?: string;
- quality?: number;
- callback?: (str: string) => void;
- }) {
+ toDataURL(
+ config?: ImageConfig & {
+ callback?: (url: string) => void;
+ }
+ ) {
config = config || {};
const mimeType = config.mimeType || null,
quality = config.quality || null;
@@ -2187,16 +2191,11 @@ export abstract class Node {
* }
* });
*/
- toImage(config?: {
- x?: number;
- y?: number;
- width?: number;
- height?: number;
- pixelRatio?: number;
- mimeType?: string;
- quality?: number;
- callback?: (img: HTMLImageElement) => void;
- }) {
+ toImage(
+ config?: ImageConfig & {
+ callback?: (img: HTMLImageElement) => void;
+ }
+ ) {
return new Promise((resolve, reject) => {
try {
const callback = config?.callback;
@@ -2231,16 +2230,11 @@ export abstract class Node {
* var blob = await node.toBlob({});
* @returns {Promise}
*/
- toBlob(config?: {
- x?: number;
- y?: number;
- width?: number;
- height?: number;
- pixelRatio?: number;
- mimeType?: string;
- quality?: number;
- callback?: (blob: Blob | null) => void;
- }) {
+ toBlob(
+ config?: ImageConfig & {
+ callback?: (blob: Blob | null) => void;
+ }
+ ) {
return new Promise((resolve, reject) => {
try {
const callback = config?.callback;
@@ -2405,8 +2399,8 @@ export abstract class Node {
* @example
* node.setAttr('x', 5);
*/
- setAttr(attr: string, val) {
- const func = this[SET + Util._capitalize(attr)];
+ setAttr>(attr: K, val: K extends keyof AttrConfig ? AttrConfig[K] : any) {
+ const func = this[SET + Util._capitalize(attr as string)];
if (Util._isFunction(func)) {
func.call(this, val);
@@ -2422,7 +2416,7 @@ export abstract class Node {
drawNode?.batchDraw();
}
}
- _setAttr(key: string, val) {
+ _setAttr>(key: K, val: K extends keyof AttrConfig ? AttrConfig[K] : any) {
const oldVal = this.attrs[key];
if (oldVal === val && !Util.isObject(val)) {
return;
@@ -2878,7 +2872,7 @@ export abstract class Node {
}
}
-interface AnimTo extends NodeConfig {
+interface AnimTo extends NodeConfig> {
onFinish?: Function;
onUpdate?: Function;
duration?: number;
diff --git a/src/Shape.ts b/src/Shape.ts
index aeb6fa4f..b300a642 100644
--- a/src/Shape.ts
+++ b/src/Shape.ts
@@ -26,7 +26,7 @@ export type ShapeConfigHandler = {
export type LineJoin = 'round' | 'bevel' | 'miter';
export type LineCap = 'butt' | 'round' | 'square';
-export interface ShapeConfig extends NodeConfig {
+export type ShapeConfig = {}> = NodeConfig & {
fill?: string | CanvasGradient;
fillPatternImage?: HTMLImageElement;
fillPatternX?: number;
diff --git a/src/Stage.ts b/src/Stage.ts
index 36893b87..9ba1d87e 100644
--- a/src/Stage.ts
+++ b/src/Stage.ts
@@ -154,7 +154,7 @@ export const stages: Stage[] = [];
* });
*/
-export class Stage extends Container {
+export class Stage extends Container {
content: HTMLDivElement;
pointerPos: Vector2d | null;
_pointerPositions: (Vector2d & { id?: number })[] = [];
diff --git a/src/Tween.ts b/src/Tween.ts
index 7c78011b..ef7c2562 100644
--- a/src/Tween.ts
+++ b/src/Tween.ts
@@ -151,6 +151,9 @@ class TweenEngine {
}
export interface TweenConfig extends NodeConfig {
+ easing?: typeof Easings[keyof typeof Easings];
+ yoyo?: boolean;
+ onReset?: Function;
onFinish?: Function;
onUpdate?: Function;
duration?: number;
@@ -419,7 +422,7 @@ export class Tween {
// after tweening points of line we need to set original end
const attrs = Tween.attrs[node._id][this._id];
if (attrs.points && attrs.points.trueEnd) {
- node.setAttr('points', attrs.points.trueEnd);
+ node.setAttr('points' as any, attrs.points.trueEnd);
}
if (this.onFinish) {
diff --git a/src/Util.ts b/src/Util.ts
index 372d3ad2..ea59767a 100644
--- a/src/Util.ts
+++ b/src/Util.ts
@@ -1121,3 +1121,5 @@ export const Util = {
}
},
};
+
+export type AnyString = T | (string & {})
diff --git a/src/canvas-backend.ts b/src/canvas-backend.ts
index f9a0bfec..35ff9aec 100644
--- a/src/canvas-backend.ts
+++ b/src/canvas-backend.ts
@@ -7,6 +7,7 @@ const canvas = Canvas['default'] || Canvas;
// @ts-ignore
global.DOMMatrix = canvas.DOMMatrix;
+// @ts-ignore
(global as any).Path2D ??= class Path2D {
constructor(path: any) {
(this as any).path = path;
diff --git a/src/shapes/Rect.ts b/src/shapes/Rect.ts
index 581f98c9..be169509 100644
--- a/src/shapes/Rect.ts
+++ b/src/shapes/Rect.ts
@@ -8,7 +8,7 @@ import type { GetSet } from '../types.ts';
import type { Context } from '../Context.ts';
import { getNumberOrArrayOfNumbersValidator } from '../Validators.ts';
-export interface RectConfig extends ShapeConfig {
+export type RectConfig = {}> = ShapeConfig & {
cornerRadius?: number | number[];
}
@@ -30,7 +30,7 @@ export interface RectConfig extends ShapeConfig {
* strokeWidth: 5
* });
*/
-export class Rect extends Shape {
+export class Rect = {}> extends Shape> {
_sceneFunc(context: Context) {
const cornerRadius = this.cornerRadius(),
width = this.width(),
diff --git a/src/skia-backend.ts b/src/skia-backend.ts
index 7619e6b0..7856e7da 100644
--- a/src/skia-backend.ts
+++ b/src/skia-backend.ts
@@ -2,8 +2,10 @@ import { Konva } from './_CoreInternals.ts';
// @ts-ignore
import { Canvas, DOMMatrix, Image, Path2D } from 'skia-canvas';
+// @ts-ignore
global.DOMMatrix = DOMMatrix as any;
+// @ts-ignore
global.Path2D = Path2D as any;
Path2D.prototype.toString = () => '[object Path2D]';