mirror of
https://github.com/konvajs/konva.git
synced 2025-11-24 08:46:44 +08:00
Merge branch 'master' of github.com:konvajs/konva
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -20,6 +20,7 @@ types
|
|||||||
out.png
|
out.png
|
||||||
cmj
|
cmj
|
||||||
.test-temp
|
.test-temp
|
||||||
|
.history
|
||||||
|
|
||||||
# Numerous always-ignore extensions
|
# Numerous always-ignore extensions
|
||||||
*.diff
|
*.diff
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://konvajs.org/android-chrome-192x192.png" alt="Konva logo" height="180" />
|
<img src="https://konvajs.org/img/icon.png" alt="Konva logo" height="60" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1 align="center">Konva</h1>
|
<h1 align="center">Konva</h1>
|
||||||
|
|||||||
@@ -44,13 +44,13 @@
|
|||||||
"test": "npm run test:browser && npm run test:node && npm run test:import",
|
"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: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: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: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: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",
|
"test:node": "npm run test:node:canvas && npm run test:node:skia",
|
||||||
"tsc": "tsc --removeComments",
|
"tsc": "tsc --removeComments",
|
||||||
"rollup": "rollup -c",
|
"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",
|
"watch": "rollup -c -w",
|
||||||
"size": "size-limit"
|
"size": "size-limit"
|
||||||
},
|
},
|
||||||
@@ -107,6 +107,7 @@
|
|||||||
"parcel": "2.15.4",
|
"parcel": "2.15.4",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
|
"rimraf": "^6.0.1",
|
||||||
"rollup": "^4.48.0",
|
"rollup": "^4.48.0",
|
||||||
"rollup-plugin-typescript2": "^0.36.0",
|
"rollup-plugin-typescript2": "^0.36.0",
|
||||||
"size-limit": "^11.2.0",
|
"size-limit": "^11.2.0",
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ export interface ContainerConfig extends NodeConfig {
|
|||||||
*/
|
*/
|
||||||
export abstract class Container<
|
export abstract class Container<
|
||||||
ChildType extends Node = Node,
|
ChildType extends Node = Node,
|
||||||
> extends Node<ContainerConfig> {
|
Config extends ContainerConfig = ContainerConfig
|
||||||
|
> extends Node<Config> {
|
||||||
children: Array<ChildType> = [];
|
children: Array<ChildType> = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ export const Konva = {
|
|||||||
_injectGlobal(Konva) {
|
_injectGlobal(Konva) {
|
||||||
if (typeof glob.Konva !== 'undefined') {
|
if (typeof glob.Konva !== 'undefined') {
|
||||||
console.error(
|
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;
|
glob.Konva = Konva;
|
||||||
|
|||||||
108
src/Node.ts
108
src/Node.ts
@@ -10,7 +10,7 @@ import type { Layer } from './Layer.ts';
|
|||||||
import type { Shape } from './Shape.ts';
|
import type { Shape } from './Shape.ts';
|
||||||
import type { Stage } from './Stage.ts';
|
import type { Stage } from './Stage.ts';
|
||||||
import type { GetSet, IRect, Vector2d } from './types.ts';
|
import type { GetSet, IRect, Vector2d } from './types.ts';
|
||||||
import { Transform, Util } from './Util.ts';
|
import { Transform, Util, type AnyString } from './Util.ts';
|
||||||
import {
|
import {
|
||||||
getBooleanValidator,
|
getBooleanValidator,
|
||||||
getNumberValidator,
|
getNumberValidator,
|
||||||
@@ -134,9 +134,8 @@ type globalCompositeOperationType =
|
|||||||
| 'color'
|
| 'color'
|
||||||
| 'luminosity';
|
| 'luminosity';
|
||||||
|
|
||||||
export interface NodeConfig {
|
// allow any custom attribute
|
||||||
// allow any custom attribute
|
export type NodeConfig<Props extends Record<string, any> = {}> = Props & {
|
||||||
[index: string]: any;
|
|
||||||
x?: number;
|
x?: number;
|
||||||
y?: number;
|
y?: number;
|
||||||
width?: number;
|
width?: number;
|
||||||
@@ -223,6 +222,20 @@ export type KonvaEventListener<This, EventType> = (
|
|||||||
ev: KonvaEventObject<EventType, This>
|
ev: KonvaEventObject<EventType, This>
|
||||||
) => void;
|
) => 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,
|
* Node constructor. Nodes are entities that can be transformed, layered,
|
||||||
* and have bound events. The stage, layers, groups, and shapes all extend Node.
|
* and have bound events. The stage, layers, groups, and shapes all extend Node.
|
||||||
@@ -393,17 +406,13 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
* drawBorder: true
|
* drawBorder: true
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
cache(config?: {
|
cache(
|
||||||
x?: number;
|
config?: CanvasConfig & {
|
||||||
y?: number;
|
drawBorder?: boolean;
|
||||||
width?: number;
|
offset?: number;
|
||||||
height?: number;
|
hitCanvasPixelRatio?: number;
|
||||||
drawBorder?: boolean;
|
}
|
||||||
offset?: number;
|
) {
|
||||||
pixelRatio?: number;
|
|
||||||
imageSmoothingEnabled?: boolean;
|
|
||||||
hitCanvasPixelRatio?: number;
|
|
||||||
}) {
|
|
||||||
const conf = config || {};
|
const conf = config || {};
|
||||||
let rect = {} as IRect;
|
let rect = {} as IRect;
|
||||||
|
|
||||||
@@ -1015,13 +1024,13 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
* @example
|
* @example
|
||||||
* var x = node.getAttr('x');
|
* var x = node.getAttr('x');
|
||||||
*/
|
*/
|
||||||
getAttr<T>(attr: string) {
|
getAttr<AttrConfig extends Config, K extends AnyString<keyof Config>>(attr: K): K extends keyof AttrConfig ? AttrConfig[K] : any {
|
||||||
const method = 'get' + Util._capitalize(attr);
|
const method = 'get' + Util._capitalize(attr as string);
|
||||||
if (Util._isFunction((this as any)[method])) {
|
if (Util._isFunction((this as any)[method])) {
|
||||||
return (this as any)[method]();
|
return (this as any)[method]();
|
||||||
}
|
}
|
||||||
// otherwise get directly
|
// otherwise get directly
|
||||||
return this.attrs[attr] as T | undefined;
|
return this.attrs[attr];
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* get ancestors
|
* get ancestors
|
||||||
@@ -1050,8 +1059,8 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
* @name Konva.Node#getAttrs
|
* @name Konva.Node#getAttrs
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
getAttrs() {
|
getAttrs(): Config {
|
||||||
return (this.attrs || {}) as Config & Record<string, any>;
|
return (this.attrs || {});
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* set multiple attrs at once using an object literal
|
* set multiple attrs at once using an object literal
|
||||||
@@ -1065,7 +1074,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
* fill: 'red'
|
* fill: 'red'
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
setAttrs(config: any) {
|
setAttrs(config?: Config) {
|
||||||
this._batchTransformChanges(() => {
|
this._batchTransformChanges(() => {
|
||||||
let key, method;
|
let key, method;
|
||||||
if (!config) {
|
if (!config) {
|
||||||
@@ -1624,7 +1633,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
toObject() {
|
toObject() {
|
||||||
let attrs = this.getAttrs() as any,
|
let attrs = this.getAttrs(),
|
||||||
key,
|
key,
|
||||||
val,
|
val,
|
||||||
getter,
|
getter,
|
||||||
@@ -2111,7 +2120,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
* @example
|
* @example
|
||||||
* var canvas = node.toCanvas();
|
* var canvas = node.toCanvas();
|
||||||
*/
|
*/
|
||||||
toCanvas(config?) {
|
toCanvas(config?: CanvasConfig) {
|
||||||
return this._toKonvaCanvas(config)._canvas;
|
return this._toKonvaCanvas(config)._canvas;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -2137,16 +2146,11 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
* @param {Boolean} [config.imageSmoothingEnabled] set this to false if you want to disable imageSmoothing
|
* @param {Boolean} [config.imageSmoothingEnabled] set this to false if you want to disable imageSmoothing
|
||||||
* @returns {String}
|
* @returns {String}
|
||||||
*/
|
*/
|
||||||
toDataURL(config?: {
|
toDataURL(
|
||||||
x?: number;
|
config?: ImageConfig & {
|
||||||
y?: number;
|
callback?: (url: string) => void;
|
||||||
width?: number;
|
}
|
||||||
height?: number;
|
) {
|
||||||
pixelRatio?: number;
|
|
||||||
mimeType?: string;
|
|
||||||
quality?: number;
|
|
||||||
callback?: (str: string) => void;
|
|
||||||
}) {
|
|
||||||
config = config || {};
|
config = config || {};
|
||||||
const mimeType = config.mimeType || null,
|
const mimeType = config.mimeType || null,
|
||||||
quality = config.quality || null;
|
quality = config.quality || null;
|
||||||
@@ -2187,16 +2191,11 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
* }
|
* }
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
toImage(config?: {
|
toImage(
|
||||||
x?: number;
|
config?: ImageConfig & {
|
||||||
y?: number;
|
callback?: (img: HTMLImageElement) => void;
|
||||||
width?: number;
|
}
|
||||||
height?: number;
|
) {
|
||||||
pixelRatio?: number;
|
|
||||||
mimeType?: string;
|
|
||||||
quality?: number;
|
|
||||||
callback?: (img: HTMLImageElement) => void;
|
|
||||||
}) {
|
|
||||||
return new Promise<HTMLImageElement>((resolve, reject) => {
|
return new Promise<HTMLImageElement>((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const callback = config?.callback;
|
const callback = config?.callback;
|
||||||
@@ -2231,16 +2230,11 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
* var blob = await node.toBlob({});
|
* var blob = await node.toBlob({});
|
||||||
* @returns {Promise<Blob>}
|
* @returns {Promise<Blob>}
|
||||||
*/
|
*/
|
||||||
toBlob(config?: {
|
toBlob(
|
||||||
x?: number;
|
config?: ImageConfig & {
|
||||||
y?: number;
|
callback?: (blob: Blob | null) => void;
|
||||||
width?: number;
|
}
|
||||||
height?: number;
|
) {
|
||||||
pixelRatio?: number;
|
|
||||||
mimeType?: string;
|
|
||||||
quality?: number;
|
|
||||||
callback?: (blob: Blob | null) => void;
|
|
||||||
}) {
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const callback = config?.callback;
|
const callback = config?.callback;
|
||||||
@@ -2405,8 +2399,8 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
* @example
|
* @example
|
||||||
* node.setAttr('x', 5);
|
* node.setAttr('x', 5);
|
||||||
*/
|
*/
|
||||||
setAttr(attr: string, val) {
|
setAttr<AttrConfig extends Config, K extends AnyString<keyof Config>>(attr: K, val: K extends keyof AttrConfig ? AttrConfig[K] : any) {
|
||||||
const func = this[SET + Util._capitalize(attr)];
|
const func = this[SET + Util._capitalize(attr as string)];
|
||||||
|
|
||||||
if (Util._isFunction(func)) {
|
if (Util._isFunction(func)) {
|
||||||
func.call(this, val);
|
func.call(this, val);
|
||||||
@@ -2422,7 +2416,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
drawNode?.batchDraw();
|
drawNode?.batchDraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_setAttr(key: string, val) {
|
_setAttr<AttrConfig extends Config, K extends AnyString<keyof Config>>(key: K, val: K extends keyof AttrConfig ? AttrConfig[K] : any) {
|
||||||
const oldVal = this.attrs[key];
|
const oldVal = this.attrs[key];
|
||||||
if (oldVal === val && !Util.isObject(val)) {
|
if (oldVal === val && !Util.isObject(val)) {
|
||||||
return;
|
return;
|
||||||
@@ -2878,7 +2872,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AnimTo extends NodeConfig {
|
interface AnimTo extends NodeConfig<Record<string, any>> {
|
||||||
onFinish?: Function;
|
onFinish?: Function;
|
||||||
onUpdate?: Function;
|
onUpdate?: Function;
|
||||||
duration?: number;
|
duration?: number;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export type ShapeConfigHandler<TTarget> = {
|
|||||||
export type LineJoin = 'round' | 'bevel' | 'miter';
|
export type LineJoin = 'round' | 'bevel' | 'miter';
|
||||||
export type LineCap = 'butt' | 'round' | 'square';
|
export type LineCap = 'butt' | 'round' | 'square';
|
||||||
|
|
||||||
export interface ShapeConfig extends NodeConfig {
|
export type ShapeConfig<Props extends Record<string, any> = {}> = NodeConfig<Props> & {
|
||||||
fill?: string | CanvasGradient;
|
fill?: string | CanvasGradient;
|
||||||
fillPatternImage?: HTMLImageElement;
|
fillPatternImage?: HTMLImageElement;
|
||||||
fillPatternX?: number;
|
fillPatternX?: number;
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ export const stages: Stage[] = [];
|
|||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class Stage extends Container<Layer> {
|
export class Stage extends Container<Layer, StageConfig> {
|
||||||
content: HTMLDivElement;
|
content: HTMLDivElement;
|
||||||
pointerPos: Vector2d | null;
|
pointerPos: Vector2d | null;
|
||||||
_pointerPositions: (Vector2d & { id?: number })[] = [];
|
_pointerPositions: (Vector2d & { id?: number })[] = [];
|
||||||
|
|||||||
@@ -151,6 +151,9 @@ class TweenEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface TweenConfig extends NodeConfig {
|
export interface TweenConfig extends NodeConfig {
|
||||||
|
easing?: typeof Easings[keyof typeof Easings];
|
||||||
|
yoyo?: boolean;
|
||||||
|
onReset?: Function;
|
||||||
onFinish?: Function;
|
onFinish?: Function;
|
||||||
onUpdate?: Function;
|
onUpdate?: Function;
|
||||||
duration?: number;
|
duration?: number;
|
||||||
@@ -419,7 +422,7 @@ export class Tween {
|
|||||||
// after tweening points of line we need to set original end
|
// after tweening points of line we need to set original end
|
||||||
const attrs = Tween.attrs[node._id][this._id];
|
const attrs = Tween.attrs[node._id][this._id];
|
||||||
if (attrs.points && attrs.points.trueEnd) {
|
if (attrs.points && attrs.points.trueEnd) {
|
||||||
node.setAttr('points', attrs.points.trueEnd);
|
node.setAttr('points' as any, attrs.points.trueEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.onFinish) {
|
if (this.onFinish) {
|
||||||
|
|||||||
@@ -1121,3 +1121,5 @@ export const Util = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AnyString<T> = T | (string & {})
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const canvas = Canvas['default'] || Canvas;
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
global.DOMMatrix = canvas.DOMMatrix;
|
global.DOMMatrix = canvas.DOMMatrix;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
(global as any).Path2D ??= class Path2D {
|
(global as any).Path2D ??= class Path2D {
|
||||||
constructor(path: any) {
|
constructor(path: any) {
|
||||||
(this as any).path = path;
|
(this as any).path = path;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import type { GetSet } from '../types.ts';
|
|||||||
import type { Context } from '../Context.ts';
|
import type { Context } from '../Context.ts';
|
||||||
import { getNumberOrArrayOfNumbersValidator } from '../Validators.ts';
|
import { getNumberOrArrayOfNumbersValidator } from '../Validators.ts';
|
||||||
|
|
||||||
export interface RectConfig extends ShapeConfig {
|
export type RectConfig<Props extends Record<string, any> = {}> = ShapeConfig<Props> & {
|
||||||
cornerRadius?: number | number[];
|
cornerRadius?: number | number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ export interface RectConfig extends ShapeConfig {
|
|||||||
* strokeWidth: 5
|
* strokeWidth: 5
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
export class Rect extends Shape<RectConfig> {
|
export class Rect<Props extends Record<string, any> = {}> extends Shape<RectConfig<Props>> {
|
||||||
_sceneFunc(context: Context) {
|
_sceneFunc(context: Context) {
|
||||||
const cornerRadius = this.cornerRadius(),
|
const cornerRadius = this.cornerRadius(),
|
||||||
width = this.width(),
|
width = this.width(),
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ import { Konva } from './_CoreInternals.ts';
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { Canvas, DOMMatrix, Image, Path2D } from 'skia-canvas';
|
import { Canvas, DOMMatrix, Image, Path2D } from 'skia-canvas';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
global.DOMMatrix = DOMMatrix as any;
|
global.DOMMatrix = DOMMatrix as any;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
global.Path2D = Path2D as any;
|
global.Path2D = Path2D as any;
|
||||||
Path2D.prototype.toString = () => '[object Path2D]';
|
Path2D.prototype.toString = () => '[object Path2D]';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user