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
|
||||
cmj
|
||||
.test-temp
|
||||
.history
|
||||
|
||||
# Numerous always-ignore extensions
|
||||
*.diff
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<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>
|
||||
|
||||
<h1 align="center">Konva</h1>
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -32,7 +32,8 @@ export interface ContainerConfig extends NodeConfig {
|
||||
*/
|
||||
export abstract class Container<
|
||||
ChildType extends Node = Node,
|
||||
> extends Node<ContainerConfig> {
|
||||
Config extends ContainerConfig = ContainerConfig
|
||||
> extends Node<Config> {
|
||||
children: Array<ChildType> = [];
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
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 { 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 extends Record<string, any> = {}> = Props & {
|
||||
x?: number;
|
||||
y?: number;
|
||||
width?: number;
|
||||
@@ -223,6 +222,20 @@ export type KonvaEventListener<This, EventType> = (
|
||||
ev: KonvaEventObject<EventType, This>
|
||||
) => 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<Config extends NodeConfig = NodeConfig> {
|
||||
* 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<Config extends NodeConfig = NodeConfig> {
|
||||
* @example
|
||||
* var x = node.getAttr('x');
|
||||
*/
|
||||
getAttr<T>(attr: string) {
|
||||
const method = 'get' + Util._capitalize(attr);
|
||||
getAttr<AttrConfig extends Config, K extends AnyString<keyof Config>>(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<Config extends NodeConfig = NodeConfig> {
|
||||
* @name Konva.Node#getAttrs
|
||||
* @returns {Object}
|
||||
*/
|
||||
getAttrs() {
|
||||
return (this.attrs || {}) as Config & Record<string, any>;
|
||||
getAttrs(): Config {
|
||||
return (this.attrs || {});
|
||||
}
|
||||
/**
|
||||
* set multiple attrs at once using an object literal
|
||||
@@ -1065,7 +1074,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
||||
* fill: 'red'
|
||||
* });
|
||||
*/
|
||||
setAttrs(config: any) {
|
||||
setAttrs(config?: Config) {
|
||||
this._batchTransformChanges(() => {
|
||||
let key, method;
|
||||
if (!config) {
|
||||
@@ -1624,7 +1633,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
||||
* @returns {Object}
|
||||
*/
|
||||
toObject() {
|
||||
let attrs = this.getAttrs() as any,
|
||||
let attrs = this.getAttrs(),
|
||||
key,
|
||||
val,
|
||||
getter,
|
||||
@@ -2111,7 +2120,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
||||
* @example
|
||||
* var canvas = node.toCanvas();
|
||||
*/
|
||||
toCanvas(config?) {
|
||||
toCanvas(config?: CanvasConfig) {
|
||||
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
|
||||
* @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<Config extends NodeConfig = NodeConfig> {
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
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<HTMLImageElement>((resolve, reject) => {
|
||||
try {
|
||||
const callback = config?.callback;
|
||||
@@ -2231,16 +2230,11 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
||||
* var blob = await node.toBlob({});
|
||||
* @returns {Promise<Blob>}
|
||||
*/
|
||||
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<Config extends NodeConfig = NodeConfig> {
|
||||
* @example
|
||||
* node.setAttr('x', 5);
|
||||
*/
|
||||
setAttr(attr: string, val) {
|
||||
const func = this[SET + Util._capitalize(attr)];
|
||||
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 as string)];
|
||||
|
||||
if (Util._isFunction(func)) {
|
||||
func.call(this, val);
|
||||
@@ -2422,7 +2416,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
||||
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];
|
||||
if (oldVal === val && !Util.isObject(val)) {
|
||||
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;
|
||||
onUpdate?: Function;
|
||||
duration?: number;
|
||||
|
||||
@@ -26,7 +26,7 @@ export type ShapeConfigHandler<TTarget> = {
|
||||
export type LineJoin = 'round' | 'bevel' | 'miter';
|
||||
export type LineCap = 'butt' | 'round' | 'square';
|
||||
|
||||
export interface ShapeConfig extends NodeConfig {
|
||||
export type ShapeConfig<Props extends Record<string, any> = {}> = NodeConfig<Props> & {
|
||||
fill?: string | CanvasGradient;
|
||||
fillPatternImage?: HTMLImageElement;
|
||||
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;
|
||||
pointerPos: Vector2d | null;
|
||||
_pointerPositions: (Vector2d & { id?: number })[] = [];
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1121,3 +1121,5 @@ export const Util = {
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export type AnyString<T> = T | (string & {})
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Props extends Record<string, any> = {}> = ShapeConfig<Props> & {
|
||||
cornerRadius?: number | number[];
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export interface RectConfig extends ShapeConfig {
|
||||
* strokeWidth: 5
|
||||
* });
|
||||
*/
|
||||
export class Rect extends Shape<RectConfig> {
|
||||
export class Rect<Props extends Record<string, any> = {}> extends Shape<RectConfig<Props>> {
|
||||
_sceneFunc(context: Context) {
|
||||
const cornerRadius = this.cornerRadius(),
|
||||
width = this.width(),
|
||||
|
||||
@@ -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]';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user