mirror of
https://github.com/konvajs/konva.git
synced 2025-11-24 16:53:06 +08:00
Compare commits
79 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b99aa9813 | ||
|
|
331bd9bb64 | ||
|
|
72d92d8bcc | ||
|
|
459606b637 | ||
|
|
3f6f9f9365 | ||
|
|
ac1587fac8 | ||
|
|
00997bc225 | ||
|
|
5ac40635e4 | ||
|
|
d9e6f11e42 | ||
|
|
bb5275f9d1 | ||
|
|
cb69ff9223 | ||
|
|
e86f222253 | ||
|
|
addbb98328 | ||
|
|
83523ecc6f | ||
|
|
8be222e80e | ||
|
|
0a99665e98 | ||
|
|
d95ff79964 | ||
|
|
af13fc2e11 | ||
|
|
cb0cbbfe3d | ||
|
|
52977b5c0c | ||
|
|
01f4bb605e | ||
|
|
b54f3888e3 | ||
|
|
68b4ea3cb6 | ||
|
|
ad01e860d9 | ||
|
|
10487e99e4 | ||
|
|
f0a7924949 | ||
|
|
9083dfbbe7 | ||
|
|
f2a37285fc | ||
|
|
a5cd67261f | ||
|
|
e30a16cb36 | ||
|
|
e2009f58ff | ||
|
|
bbf945b219 | ||
|
|
60aefdfeaa | ||
|
|
07bdf49bfd | ||
|
|
d089066a30 | ||
|
|
955705898d | ||
|
|
f63e019475 | ||
|
|
ed5a11dda3 | ||
|
|
7d5ba3e429 | ||
|
|
2587e0708f | ||
|
|
5143154d97 | ||
|
|
8f2e9f8793 | ||
|
|
0ec3425d99 | ||
|
|
fdd0e64aad | ||
|
|
88861b3ec6 | ||
|
|
70f57d2a95 | ||
|
|
6d34326084 | ||
|
|
03ad744ab7 | ||
|
|
7f607b1b7e | ||
|
|
ef8710fd48 | ||
|
|
4f29c6d365 | ||
|
|
613493eb21 | ||
|
|
b0b8347810 | ||
|
|
ab224d5b85 | ||
|
|
73d65cb7d3 | ||
|
|
b09ceb34f3 | ||
|
|
fe8b7e0fc3 | ||
|
|
9f8c653745 | ||
|
|
8cdac23faa | ||
|
|
eb4e2b4fef | ||
|
|
e7672858b7 | ||
|
|
ea92753221 | ||
|
|
93106aba53 | ||
|
|
4da037a2fa | ||
|
|
1e2c28738b | ||
|
|
f212a1ad69 | ||
|
|
b37e14afff | ||
|
|
76cace8b38 | ||
|
|
abfdf7153f | ||
|
|
297241add4 | ||
|
|
80fad79858 | ||
|
|
0d502baccd | ||
|
|
a1660e1ccb | ||
|
|
48ae639b70 | ||
|
|
f6ccabb34a | ||
|
|
49e6e3e824 | ||
|
|
467147b4c7 | ||
|
|
a8efcd554a | ||
|
|
813cd4f780 |
55
CHANGELOG.md
55
CHANGELOG.md
@@ -3,6 +3,61 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
### 9.3.14 (2024-07-16)
|
||||
|
||||
- Fix shadow + corner radius for images
|
||||
- Support `fillRule` for `Konva.Shape` on hit graph
|
||||
|
||||
### 9.3.13 (2024-07-05)
|
||||
|
||||
- Fallback for `Konva.Text.measureSize()` when 2d context doesn't return full data
|
||||
|
||||
### 9.3.12 (2024-06-20)
|
||||
|
||||
- Fix stopped transforming when it was triggered by multi-touch
|
||||
- Fix svg `path.getPointAtLength()` calculations in some cases
|
||||
- Fix `shape.getClientRect()` when any of parents is cached
|
||||
|
||||
### 9.3.11 (2024-05-23)
|
||||
|
||||
- Fix chrome clear canvas issue
|
||||
- Typescript fixes
|
||||
|
||||
### 9.3.9 (2024-05-20)
|
||||
|
||||
- Fix underline and line-through for `Konva.Text` when `Konva._fixTextRendering = true`
|
||||
|
||||
### 9.3.8 (2024-05-15)
|
||||
|
||||
- Fix click events fires on stage
|
||||
- Temporary `Konva._fixTextRendering = true` flag to fix inconsistent text
|
||||
|
||||
### 9.3.6 (2024-03-04)
|
||||
|
||||
- Fix transformer bug to enable hit graph back
|
||||
|
||||
### 9.3.5 (2024-03-04)
|
||||
|
||||
- `tranformer` event will be triggered AFTER all data of transformer is updated
|
||||
- Improve performance of transformer
|
||||
|
||||
### 9.3.4 (2024-03-03)
|
||||
|
||||
- Fix clipping with zero size
|
||||
|
||||
### 9.3.3 (2024-02-09)
|
||||
|
||||
- Another fix for exporting buffered shapes
|
||||
|
||||
### 9.3.2 (2024-01-26)
|
||||
|
||||
- Fix large memory usage on node export
|
||||
|
||||
### 9.3.1 (2024-01-17)
|
||||
|
||||
- Fix Pixelate filter work/fix caching size
|
||||
- Fix node export when large buffer canvas is used
|
||||
|
||||
### 9.3.0 (2023-12-20)
|
||||
|
||||
- New attribute `rotateLineVisible` for `Konva.Transformer` to show/hide rotate line
|
||||
|
||||
@@ -64,6 +64,10 @@ Konva works in all modern mobile and desktop browsers. A browser need to be capa
|
||||
|
||||
At the current moment `Konva` doesn't work in IE11 directly. To make it work you just need to provide some polyfills such as `Array.prototype.find`, `String.prototype.trimLeft`, `String.prototype.trimRight`, `Array.from`.
|
||||
|
||||
# Debugging
|
||||
|
||||
The Chrome inspector simply shows the canvas element. To see the Konva objects and their details, install the konva-dev extension at https://github.com/konvajs/konva-devtool.
|
||||
|
||||
# Loading and installing Konva
|
||||
|
||||
Konva supports UMD loading. So you can use all possible variants to load the framework into your project:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "konva",
|
||||
"version": "9.3.0",
|
||||
"version": "9.3.14",
|
||||
"author": "Anton Lavrenov",
|
||||
"files": [
|
||||
"README.md",
|
||||
|
||||
@@ -270,7 +270,7 @@ export abstract class Container<
|
||||
obj.children = [];
|
||||
|
||||
this.getChildren().forEach((child) => {
|
||||
obj.children.push(child.toObject());
|
||||
obj.children!.push(child.toObject());
|
||||
});
|
||||
|
||||
return obj;
|
||||
@@ -307,6 +307,7 @@ export abstract class Container<
|
||||
* canvas and redraw every shape inside the container, it should only be used for special situations
|
||||
* because it performs very poorly. Please use the {@link Konva.Stage#getIntersection} method if at all possible
|
||||
* because it performs much better
|
||||
* nodes with listening set to false will not be detected
|
||||
* @method
|
||||
* @name Konva.Container#getAllIntersections
|
||||
* @param {Object} pos
|
||||
@@ -342,7 +343,7 @@ export abstract class Container<
|
||||
});
|
||||
this._requestDraw();
|
||||
}
|
||||
drawScene(can?: SceneCanvas, top?: Node) {
|
||||
drawScene(can?: SceneCanvas, top?: Node, bufferCanvas?: SceneCanvas) {
|
||||
var layer = this.getLayer()!,
|
||||
canvas = can || (layer && layer.getCanvas()),
|
||||
context = canvas && canvas.getContext(),
|
||||
@@ -361,7 +362,7 @@ export abstract class Container<
|
||||
this._drawCachedSceneCanvas(context);
|
||||
context.restore();
|
||||
} else {
|
||||
this._drawChildren('drawScene', canvas, top);
|
||||
this._drawChildren('drawScene', canvas, top, bufferCanvas);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -387,12 +388,14 @@ export abstract class Container<
|
||||
}
|
||||
return this;
|
||||
}
|
||||
_drawChildren(drawMethod, canvas, top) {
|
||||
_drawChildren(drawMethod, canvas, top, bufferCanvas?) {
|
||||
var context = canvas && canvas.getContext(),
|
||||
clipWidth = this.clipWidth(),
|
||||
clipHeight = this.clipHeight(),
|
||||
clipFunc = this.clipFunc(),
|
||||
hasClip = (clipWidth && clipHeight) || clipFunc;
|
||||
hasClip =
|
||||
(typeof clipWidth === 'number' && typeof clipHeight === 'number') ||
|
||||
clipFunc;
|
||||
|
||||
const selfCache = top === this;
|
||||
|
||||
@@ -408,7 +411,7 @@ export abstract class Container<
|
||||
} else {
|
||||
var clipX = this.clipX();
|
||||
var clipY = this.clipY();
|
||||
context.rect(clipX, clipY, clipWidth, clipHeight);
|
||||
context.rect(clipX || 0, clipY || 0, clipWidth, clipHeight);
|
||||
}
|
||||
context.clip.apply(context, clipArgs);
|
||||
m = transform.copy().invert().getMatrix();
|
||||
@@ -426,7 +429,7 @@ export abstract class Container<
|
||||
}
|
||||
|
||||
this.children?.forEach(function (child) {
|
||||
child[drawMethod](canvas, top);
|
||||
child[drawMethod](canvas, top, bufferCanvas);
|
||||
});
|
||||
if (hasComposition) {
|
||||
context.restore();
|
||||
|
||||
@@ -58,6 +58,7 @@ var COMMA = ',',
|
||||
'putImageData',
|
||||
'quadraticCurveTo',
|
||||
'rect',
|
||||
'roundRect',
|
||||
'restore',
|
||||
'rotate',
|
||||
'save',
|
||||
@@ -586,6 +587,20 @@ export class Context {
|
||||
rect(x: number, y: number, width: number, height: number) {
|
||||
this._context.rect(x, y, width, height);
|
||||
}
|
||||
/**
|
||||
* roundRect function.
|
||||
* @method
|
||||
* @name Konva.Context#roundRect
|
||||
*/
|
||||
roundRect(
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
radii: number | DOMPointInit | (number | DOMPointInit)[]
|
||||
) {
|
||||
this._context.roundRect(x, y, width, height, radii);
|
||||
}
|
||||
/**
|
||||
* putImageData function.
|
||||
* @method
|
||||
|
||||
@@ -90,6 +90,7 @@ export const Konva = {
|
||||
_mouseDblClickPointerId: null,
|
||||
_touchDblClickPointerId: null,
|
||||
_pointerDblClickPointerId: null,
|
||||
_fixTextRendering: false,
|
||||
|
||||
/**
|
||||
* Global pixel ratio configuration. KonvaJS automatically detect pixel ratio of current device.
|
||||
@@ -155,6 +156,9 @@ export const Konva = {
|
||||
isDragging() {
|
||||
return Konva['DD'].isDragging;
|
||||
},
|
||||
isTransforming() {
|
||||
return Konva['Transformer']?.isTransforming();
|
||||
},
|
||||
/**
|
||||
* returns whether or not a drag and drop operation is ready, but may
|
||||
* not necessarily have started
|
||||
|
||||
@@ -309,6 +309,7 @@ export class Layer extends Container<Group | Shape> {
|
||||
* get visible intersection shape. This is the preferred
|
||||
* method for determining if a point intersects a shape or not
|
||||
* also you may pass optional selector parameter to return ancestor of intersected shape
|
||||
* nodes with listening set to false will not be detected
|
||||
* @method
|
||||
* @name Konva.Layer#getIntersection
|
||||
* @param {Object} pos
|
||||
@@ -469,7 +470,10 @@ export class Layer extends Container<Group | Shape> {
|
||||
}
|
||||
|
||||
destroy(): this {
|
||||
Util.releaseCanvas(this.getNativeCanvasElement(), this.getHitCanvas()._canvas);
|
||||
Util.releaseCanvas(
|
||||
this.getNativeCanvasElement(),
|
||||
this.getHitCanvas()._canvas
|
||||
);
|
||||
return super.destroy();
|
||||
}
|
||||
|
||||
|
||||
62
src/Node.ts
62
src/Node.ts
@@ -60,6 +60,8 @@ export interface NodeConfig {
|
||||
opacity?: number;
|
||||
scale?: Vector2d;
|
||||
scaleX?: number;
|
||||
skewX?: number;
|
||||
skewY?: number;
|
||||
scaleY?: number;
|
||||
rotation?: number;
|
||||
rotationDeg?: number;
|
||||
@@ -341,10 +343,13 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
||||
return;
|
||||
}
|
||||
|
||||
// let's just add 1 pixel extra,
|
||||
// because using Math.floor on x, y position may shift drawing
|
||||
width += offset * 2 + 1;
|
||||
height += offset * 2 + 1;
|
||||
// to avoid shift we need to increase size
|
||||
// but we better to avoid it, for better filters flows
|
||||
const extraPaddingX = Math.abs(Math.round(rect.x) - x) > 0.5 ? 1 : 0;
|
||||
const extraPaddingY = Math.abs(Math.round(rect.y) - y) > 0.5 ? 1 : 0;
|
||||
width += offset * 2 + extraPaddingX;
|
||||
height += offset * 2 + extraPaddingY;
|
||||
|
||||
x -= offset;
|
||||
y -= offset;
|
||||
@@ -444,7 +449,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
||||
return this._cache.has(CANVAS);
|
||||
}
|
||||
|
||||
abstract drawScene(canvas?: Canvas, top?: Node): void;
|
||||
abstract drawScene(canvas?: Canvas, top?: Node, bufferCanvas?: Canvas): void;
|
||||
abstract drawHit(canvas?: Canvas, top?: Node): void;
|
||||
/**
|
||||
* Return client rectangle {x, y, width, height} of node. This rectangle also include all styling (strokes, shadows, etc).
|
||||
@@ -911,7 +916,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
||||
* @returns {Object}
|
||||
*/
|
||||
getAttrs() {
|
||||
return this.attrs || {};
|
||||
return (this.attrs || {}) as Config & Record<string, any>;
|
||||
}
|
||||
/**
|
||||
* set multiple attrs at once using an object literal
|
||||
@@ -1024,7 +1029,10 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
||||
}
|
||||
});
|
||||
|
||||
var dragSkip = !skipDragCheck && !Konva.hitOnDragEnabled && layerUnderDrag;
|
||||
var dragSkip =
|
||||
!skipDragCheck &&
|
||||
!Konva.hitOnDragEnabled &&
|
||||
(layerUnderDrag || Konva.isTransforming());
|
||||
return this.isListening() && this.isVisible() && !dragSkip;
|
||||
}
|
||||
|
||||
@@ -1481,15 +1489,21 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
||||
* @returns {Object}
|
||||
*/
|
||||
toObject() {
|
||||
var obj = {} as any,
|
||||
attrs = this.getAttrs(),
|
||||
var attrs = this.getAttrs() as any,
|
||||
key,
|
||||
val,
|
||||
getter,
|
||||
defaultValue,
|
||||
nonPlainObject;
|
||||
|
||||
obj.attrs = {};
|
||||
const obj: {
|
||||
attrs: Config & Record<string, any>;
|
||||
className: string;
|
||||
children?: Array<any>;
|
||||
} = {
|
||||
attrs: {} as Config & Record<string, any>,
|
||||
className: this.getClassName(),
|
||||
};
|
||||
|
||||
for (key in attrs) {
|
||||
val = attrs[key];
|
||||
@@ -1507,12 +1521,11 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
||||
// restore attr value
|
||||
attrs[key] = val;
|
||||
if (defaultValue !== val) {
|
||||
obj.attrs[key] = val;
|
||||
(obj.attrs as any)[key] = val;
|
||||
}
|
||||
}
|
||||
|
||||
obj.className = this.getClassName();
|
||||
return Util._prepareToStringify(obj);
|
||||
return Util._prepareToStringify(obj) as typeof obj;
|
||||
}
|
||||
/**
|
||||
* convert Node into a JSON string. Returns a JSON string.
|
||||
@@ -1922,6 +1935,15 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
||||
}),
|
||||
context = canvas.getContext();
|
||||
|
||||
const bufferCanvas = new SceneCanvas({
|
||||
// width and height already multiplied by pixelRatio
|
||||
// so we need to revert that
|
||||
// also increase size by x nd y offset to make sure content fits canvas
|
||||
width: canvas.width / canvas.pixelRatio + Math.abs(x),
|
||||
height: canvas.height / canvas.pixelRatio + Math.abs(y),
|
||||
pixelRatio: canvas.pixelRatio,
|
||||
});
|
||||
|
||||
if (config.imageSmoothingEnabled === false) {
|
||||
context._context.imageSmoothingEnabled = false;
|
||||
}
|
||||
@@ -1931,7 +1953,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
||||
context.translate(-1 * x, -1 * y);
|
||||
}
|
||||
|
||||
this.drawScene(canvas);
|
||||
this.drawScene(canvas, undefined, bufferCanvas);
|
||||
context.restore();
|
||||
|
||||
return canvas;
|
||||
@@ -2088,10 +2110,14 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
||||
try {
|
||||
const callback = config?.callback;
|
||||
if (callback) delete config.callback;
|
||||
this.toCanvas(config).toBlob((blob) => {
|
||||
resolve(blob);
|
||||
callback?.(blob);
|
||||
}, config?.mimeType, config?.quality);
|
||||
this.toCanvas(config).toBlob(
|
||||
(blob) => {
|
||||
resolve(blob);
|
||||
callback?.(blob);
|
||||
},
|
||||
config?.mimeType,
|
||||
config?.quality
|
||||
);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
@@ -3129,6 +3155,8 @@ addGetterSetter(Node, 'listening', true, getBooleanValidator());
|
||||
/**
|
||||
* get/set listening attr. If you need to determine if a node is listening or not
|
||||
* by taking into account its parents, use the isListening() method
|
||||
* nodes with listening set to false will not be detected in hit graph
|
||||
* so they will be ignored in container.getIntersection() method
|
||||
* @name Konva.Node#listening
|
||||
* @method
|
||||
* @param {Boolean} listening Can be true, or false. The default is true.
|
||||
|
||||
51
src/Shape.ts
51
src/Shape.ts
@@ -26,7 +26,7 @@ export type LineJoin = 'round' | 'bevel' | 'miter';
|
||||
export type LineCap = 'butt' | 'round' | 'square';
|
||||
|
||||
export interface ShapeConfig extends NodeConfig {
|
||||
fill?: string;
|
||||
fill?: string | CanvasGradient;
|
||||
fillPatternImage?: HTMLImageElement;
|
||||
fillPatternX?: number;
|
||||
fillPatternY?: number;
|
||||
@@ -128,8 +128,13 @@ function _fillFunc(this: Node, context) {
|
||||
function _strokeFunc(context) {
|
||||
context.stroke();
|
||||
}
|
||||
function _fillFuncHit(context) {
|
||||
context.fill();
|
||||
function _fillFuncHit(this: Node, context) {
|
||||
const fillRule = this.attrs.fillRule;
|
||||
if (fillRule) {
|
||||
context.fill(fillRule);
|
||||
} else {
|
||||
context.fill();
|
||||
}
|
||||
}
|
||||
function _strokeFuncHit(context) {
|
||||
context.stroke();
|
||||
@@ -469,10 +474,6 @@ export class Shape<
|
||||
// so they use that method with forced fill
|
||||
// it probably will be simpler, then copy/paste the code
|
||||
|
||||
// buffer canvas is available only inside the stage
|
||||
if (!this.getStage()) {
|
||||
return false;
|
||||
}
|
||||
// force skip buffer canvas
|
||||
const perfectDrawEnabled = this.attrs.perfectDrawEnabled ?? true;
|
||||
if (!perfectDrawEnabled) {
|
||||
@@ -532,9 +533,22 @@ export class Shape<
|
||||
};
|
||||
}
|
||||
getClientRect(config: ShapeGetClientRectConfig = {}) {
|
||||
// if we have a cached parent, it will use cached transform matrix
|
||||
// but we don't want to that
|
||||
let hasCachedParent = false;
|
||||
let parent = this.getParent();
|
||||
while (parent) {
|
||||
if (parent.isCached()) {
|
||||
hasCachedParent = true;
|
||||
break;
|
||||
}
|
||||
parent = parent.getParent();
|
||||
}
|
||||
const skipTransform = config.skipTransform;
|
||||
|
||||
const relativeTo = config.relativeTo;
|
||||
// force relative to stage if we have a cached parent
|
||||
const relativeTo =
|
||||
config.relativeTo || (hasCachedParent && this.getStage()) || undefined;
|
||||
|
||||
const fillRect = this.getSelfRect();
|
||||
|
||||
@@ -573,7 +587,7 @@ export class Shape<
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
drawScene(can?: SceneCanvas, top?: Node) {
|
||||
drawScene(can?: SceneCanvas, top?: Node, bufferCanvas?: SceneCanvas) {
|
||||
// basically there are 3 drawing modes
|
||||
// 1 - simple drawing when nothing is cached.
|
||||
// 2 - when we are caching current
|
||||
@@ -586,7 +600,6 @@ export class Shape<
|
||||
drawFunc = this.getSceneFunc(),
|
||||
hasShadow = this.hasShadow(),
|
||||
stage,
|
||||
bufferCanvas,
|
||||
bufferContext;
|
||||
|
||||
var skipBuffer = canvas.isCache;
|
||||
@@ -614,8 +627,8 @@ export class Shape<
|
||||
// if buffer canvas is needed
|
||||
if (this._useBufferCanvas() && !skipBuffer) {
|
||||
stage = this.getStage();
|
||||
bufferCanvas = stage.bufferCanvas;
|
||||
bufferContext = bufferCanvas.getContext();
|
||||
const bc = bufferCanvas || stage.bufferCanvas;
|
||||
bufferContext = bc.getContext();
|
||||
bufferContext.clear();
|
||||
bufferContext.save();
|
||||
bufferContext._applyLineJoin(this);
|
||||
@@ -626,20 +639,14 @@ export class Shape<
|
||||
drawFunc.call(this, bufferContext, this);
|
||||
bufferContext.restore();
|
||||
|
||||
var ratio = bufferCanvas.pixelRatio;
|
||||
var ratio = bc.pixelRatio;
|
||||
|
||||
if (hasShadow) {
|
||||
context._applyShadow(this);
|
||||
}
|
||||
context._applyOpacity(this);
|
||||
context._applyGlobalCompositeOperation(this);
|
||||
context.drawImage(
|
||||
bufferCanvas._canvas,
|
||||
0,
|
||||
0,
|
||||
bufferCanvas.width / ratio,
|
||||
bufferCanvas.height / ratio
|
||||
);
|
||||
context.drawImage(bc._canvas, 0, 0, bc.width / ratio, bc.height / ratio);
|
||||
} else {
|
||||
context._applyLineJoin(this);
|
||||
|
||||
@@ -777,7 +784,7 @@ export class Shape<
|
||||
dash: GetSet<number[], this>;
|
||||
dashEnabled: GetSet<boolean, this>;
|
||||
dashOffset: GetSet<number, this>;
|
||||
fill: GetSet<string, this>;
|
||||
fill: GetSet<string | CanvasGradient, this>;
|
||||
fillEnabled: GetSet<boolean, this>;
|
||||
fillLinearGradientColorStops: GetSet<Array<number | string>, this>;
|
||||
fillLinearGradientStartPoint: GetSet<Vector2d, this>;
|
||||
@@ -826,7 +833,7 @@ export class Shape<
|
||||
shadowOffsetY: GetSet<number, this>;
|
||||
shadowOpacity: GetSet<number, this>;
|
||||
shadowBlur: GetSet<number, this>;
|
||||
stroke: GetSet<string, this>;
|
||||
stroke: GetSet<string | CanvasGradient, this>;
|
||||
strokeEnabled: GetSet<boolean, this>;
|
||||
fillAfterStrokeEnabled: GetSet<boolean, this>;
|
||||
strokeScaleEnabled: GetSet<boolean, this>;
|
||||
|
||||
22
src/Stage.ts
22
src/Stage.ts
@@ -353,6 +353,7 @@ export class Stage extends Container<Layer> {
|
||||
/**
|
||||
* get visible intersection shape. This is the preferred
|
||||
* method for determining if a point intersects a shape or not
|
||||
* nodes with listening set to false will not be detected
|
||||
* @method
|
||||
* @name Konva.Stage#getIntersection
|
||||
* @param {Object} pos
|
||||
@@ -507,7 +508,8 @@ export class Stage extends Container<Layer> {
|
||||
this.setPointersPositions(evt);
|
||||
|
||||
var targetShape = this._getTargetShape(eventType);
|
||||
var eventsEnabled = !DD.isDragging || Konva.hitOnDragEnabled;
|
||||
var eventsEnabled =
|
||||
!(Konva.isDragging() || Konva.isTransforming()) || Konva.hitOnDragEnabled;
|
||||
if (targetShape && eventsEnabled) {
|
||||
targetShape._fireAndBubble(events.pointerout, { evt: evt });
|
||||
targetShape._fireAndBubble(events.pointerleave, { evt: evt });
|
||||
@@ -550,6 +552,7 @@ export class Stage extends Container<Layer> {
|
||||
|
||||
// no shape detected? do nothing
|
||||
if (!shape || !shape.isListening()) {
|
||||
this[eventType + 'ClickStartShape'] = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -590,12 +593,13 @@ export class Stage extends Container<Layer> {
|
||||
if (!events) {
|
||||
return;
|
||||
}
|
||||
if (DD.isDragging && DD.node!.preventDefault() && evt.cancelable) {
|
||||
if (Konva.isDragging() && DD.node!.preventDefault() && evt.cancelable) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
this.setPointersPositions(evt);
|
||||
|
||||
var eventsEnabled = !DD.isDragging || Konva.hitOnDragEnabled;
|
||||
var eventsEnabled =
|
||||
!(Konva.isDragging() || Konva.isTransforming()) || Konva.hitOnDragEnabled;
|
||||
if (!eventsEnabled) {
|
||||
return;
|
||||
}
|
||||
@@ -962,3 +966,15 @@ _registerNode(Stage);
|
||||
* stage.container(container);
|
||||
*/
|
||||
Factory.addGetterSetter(Stage, 'container');
|
||||
|
||||
// chrome is clearing canvas in inactive browser window, causing layer content to be erased
|
||||
// so let's redraw layers as soon as window becomes active
|
||||
// TODO: any other way to solve this issue?
|
||||
// TODO: should we remove it if chrome fixes the issue?
|
||||
if (Konva.isBrowser) {
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
stages.forEach((stage) => {
|
||||
stage.batchDraw();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -933,7 +933,7 @@ export const Util = {
|
||||
});
|
||||
return newStart;
|
||||
},
|
||||
_prepareToStringify(obj) {
|
||||
_prepareToStringify<T>(obj: any): T | null {
|
||||
var desc;
|
||||
|
||||
obj.visitedByCircularReferenceRemoval = true;
|
||||
|
||||
@@ -65,6 +65,11 @@ export class Image extends Shape<ImageConfig> {
|
||||
}
|
||||
}
|
||||
_useBufferCanvas() {
|
||||
const hasCornerRadius = !!this.cornerRadius();
|
||||
const hasShadow = this.hasShadow();
|
||||
if (hasCornerRadius && hasShadow) {
|
||||
return true;
|
||||
}
|
||||
return super._useBufferCanvas(true);
|
||||
}
|
||||
_sceneFunc(context: Context) {
|
||||
|
||||
@@ -320,61 +320,38 @@ export class Path extends Shape<PathConfig> {
|
||||
}
|
||||
|
||||
static getPointOnLine(dist, P1x, P1y, P2x, P2y, fromX?, fromY?) {
|
||||
if (fromX === undefined) {
|
||||
fromX = P1x;
|
||||
}
|
||||
if (fromY === undefined) {
|
||||
fromY = P1y;
|
||||
}
|
||||
fromX = fromX ?? P1x;
|
||||
fromY = fromY ?? P1y;
|
||||
|
||||
var m = (P2y - P1y) / (P2x - P1x + 0.00000001);
|
||||
var run = Math.sqrt((dist * dist) / (1 + m * m));
|
||||
if (P2x < P1x) {
|
||||
run *= -1;
|
||||
const len = this.getLineLength(P1x, P1y, P2x, P2y);
|
||||
if (len < 1e-10) {
|
||||
return { x: P1x, y: P1y };
|
||||
}
|
||||
var rise = m * run;
|
||||
var pt;
|
||||
|
||||
if (P2x === P1x) {
|
||||
// vertical line
|
||||
pt = {
|
||||
x: fromX,
|
||||
y: fromY + rise,
|
||||
};
|
||||
} else if ((fromY - P1y) / (fromX - P1x + 0.00000001) === m) {
|
||||
pt = {
|
||||
x: fromX + run,
|
||||
y: fromY + rise,
|
||||
};
|
||||
} else {
|
||||
var ix, iy;
|
||||
|
||||
var len = this.getLineLength(P1x, P1y, P2x, P2y);
|
||||
// if (len < 0.00000001) {
|
||||
// return {
|
||||
// x: P1x,
|
||||
// y: P1y,
|
||||
// };
|
||||
// }
|
||||
var u = (fromX - P1x) * (P2x - P1x) + (fromY - P1y) * (P2y - P1y);
|
||||
u = u / (len * len);
|
||||
ix = P1x + u * (P2x - P1x);
|
||||
iy = P1y + u * (P2y - P1y);
|
||||
|
||||
var pRise = this.getLineLength(fromX, fromY, ix, iy);
|
||||
var pRun = Math.sqrt(dist * dist - pRise * pRise);
|
||||
run = Math.sqrt((pRun * pRun) / (1 + m * m));
|
||||
if (P2x < P1x) {
|
||||
run *= -1;
|
||||
}
|
||||
rise = m * run;
|
||||
pt = {
|
||||
x: ix + run,
|
||||
y: iy + rise,
|
||||
};
|
||||
// Vertical line
|
||||
return { x: fromX, y: fromY + (P2y > P1y ? dist : -dist) };
|
||||
}
|
||||
|
||||
return pt;
|
||||
const m = (P2y - P1y) / (P2x - P1x);
|
||||
const run = Math.sqrt((dist * dist) / (1 + m * m)) * (P2x < P1x ? -1 : 1);
|
||||
const rise = m * run;
|
||||
|
||||
if (Math.abs(fromY - P1y - m * (fromX - P1x)) < 1e-10) {
|
||||
return { x: fromX + run, y: fromY + rise };
|
||||
}
|
||||
|
||||
const u =
|
||||
((fromX - P1x) * (P2x - P1x) + (fromY - P1y) * (P2y - P1y)) / (len * len);
|
||||
const ix = P1x + u * (P2x - P1x);
|
||||
const iy = P1y + u * (P2y - P1y);
|
||||
const pRise = this.getLineLength(fromX, fromY, ix, iy);
|
||||
const pRun = Math.sqrt(dist * dist - pRise * pRise);
|
||||
const adjustedRun =
|
||||
Math.sqrt((pRun * pRun) / (1 + m * m)) * (P2x < P1x ? -1 : 1);
|
||||
const adjustedRise = m * adjustedRun;
|
||||
|
||||
return { x: ix + adjustedRun, y: iy + adjustedRise };
|
||||
}
|
||||
|
||||
static getPointOnCubicBezier(pct, P1x, P1y, P2x, P2y, P3x, P3y, P4x, P4y) {
|
||||
|
||||
@@ -201,11 +201,19 @@ export class Text extends Shape<TextConfig> {
|
||||
shouldUnderline = textDecoration.indexOf('underline') !== -1,
|
||||
shouldLineThrough = textDecoration.indexOf('line-through') !== -1,
|
||||
n;
|
||||
|
||||
|
||||
direction = direction === INHERIT ? context.direction : direction;
|
||||
|
||||
var translateY = 0;
|
||||
var translateY = lineHeightPx / 2;
|
||||
var baseline = MIDDLE;
|
||||
if (Konva._fixTextRendering) {
|
||||
var metrics = this.measureSize('M'); // Use a sample character to get the ascent
|
||||
|
||||
baseline = 'alphabetic';
|
||||
translateY =
|
||||
(metrics.fontBoundingBoxAscent - metrics.fontBoundingBoxDescent) / 2 +
|
||||
lineHeightPx / 2;
|
||||
}
|
||||
|
||||
var lineTranslateX = 0;
|
||||
var lineTranslateY = 0;
|
||||
@@ -216,11 +224,10 @@ export class Text extends Shape<TextConfig> {
|
||||
|
||||
context.setAttr('font', this._getContextFont());
|
||||
|
||||
context.setAttr('textBaseline', MIDDLE);
|
||||
context.setAttr('textBaseline', baseline);
|
||||
|
||||
context.setAttr('textAlign', LEFT);
|
||||
|
||||
|
||||
// handle vertical alignment
|
||||
if (verticalAlign === MIDDLE) {
|
||||
alignY = (this.getHeight() - textArrLen * lineHeightPx - padding * 2) / 2;
|
||||
@@ -254,18 +261,17 @@ export class Text extends Shape<TextConfig> {
|
||||
context.save();
|
||||
context.beginPath();
|
||||
|
||||
context.moveTo(
|
||||
lineTranslateX,
|
||||
translateY + lineTranslateY + Math.round(fontSize / 2)
|
||||
);
|
||||
let yOffset = Konva._fixTextRendering
|
||||
? Math.round(fontSize / 4)
|
||||
: Math.round(fontSize / 2);
|
||||
const x = lineTranslateX;
|
||||
const y = translateY + lineTranslateY + yOffset;
|
||||
context.moveTo(x, y);
|
||||
spacesNumber = text.split(' ').length - 1;
|
||||
oneWord = spacesNumber === 0;
|
||||
lineWidth =
|
||||
align === JUSTIFY && !lastLine ? totalWidth - padding * 2 : width;
|
||||
context.lineTo(
|
||||
lineTranslateX + Math.round(lineWidth),
|
||||
translateY + lineTranslateY + Math.round(fontSize / 2)
|
||||
);
|
||||
context.lineTo(x + Math.round(lineWidth), y);
|
||||
|
||||
// I have no idea what is real ratio
|
||||
// just /15 looks good enough
|
||||
@@ -279,7 +285,8 @@ export class Text extends Shape<TextConfig> {
|
||||
if (shouldLineThrough) {
|
||||
context.save();
|
||||
context.beginPath();
|
||||
context.moveTo(lineTranslateX, translateY + lineTranslateY);
|
||||
let yOffset = Konva._fixTextRendering ? -Math.round(fontSize / 4) : 0;
|
||||
context.moveTo(lineTranslateX, translateY + lineTranslateY + yOffset);
|
||||
spacesNumber = text.split(' ').length - 1;
|
||||
oneWord = spacesNumber === 0;
|
||||
lineWidth =
|
||||
@@ -288,7 +295,7 @@ export class Text extends Shape<TextConfig> {
|
||||
: width;
|
||||
context.lineTo(
|
||||
lineTranslateX + Math.round(lineWidth),
|
||||
translateY + lineTranslateY
|
||||
translateY + lineTranslateY + yOffset
|
||||
);
|
||||
context.lineWidth = fontSize / 15;
|
||||
const gradient = this._getLinearGradient();
|
||||
@@ -398,9 +405,30 @@ export class Text extends Shape<TextConfig> {
|
||||
|
||||
metrics = _context.measureText(text);
|
||||
_context.restore();
|
||||
|
||||
// Scale the fallback values based on the provided fontSize compared to the sample size (100 in your new case)
|
||||
const scaleFactor = fontSize / 100;
|
||||
|
||||
// Note, fallback values are from chrome browser with 100px font size and font-family "Arial"
|
||||
return {
|
||||
actualBoundingBoxAscent:
|
||||
metrics.actualBoundingBoxAscent ?? 71.58203125 * scaleFactor,
|
||||
actualBoundingBoxDescent: metrics.actualBoundingBoxDescent ?? 0, // Remains zero as there is no descent in the provided metrics
|
||||
actualBoundingBoxLeft:
|
||||
metrics.actualBoundingBoxLeft ?? -7.421875 * scaleFactor,
|
||||
actualBoundingBoxRight:
|
||||
metrics.actualBoundingBoxRight ?? 75.732421875 * scaleFactor,
|
||||
alphabeticBaseline: metrics.alphabeticBaseline ?? 0, // Remains zero as it's typically relative to the baseline itself
|
||||
emHeightAscent: metrics.emHeightAscent ?? 100 * scaleFactor,
|
||||
emHeightDescent: metrics.emHeightDescent ?? -20 * scaleFactor,
|
||||
fontBoundingBoxAscent: metrics.fontBoundingBoxAscent ?? 91 * scaleFactor,
|
||||
fontBoundingBoxDescent:
|
||||
metrics.fontBoundingBoxDescent ?? 21 * scaleFactor,
|
||||
hangingBaseline:
|
||||
metrics.hangingBaseline ?? 72.80000305175781 * scaleFactor,
|
||||
ideographicBaseline: metrics.ideographicBaseline ?? -21 * scaleFactor,
|
||||
width: metrics.width,
|
||||
height: fontSize,
|
||||
height: fontSize, // Typically set to the font size
|
||||
};
|
||||
}
|
||||
_getContextFont() {
|
||||
@@ -703,7 +731,6 @@ Factory.overWriteSetter(Text, 'width', getNumberOrAutoValidator());
|
||||
|
||||
Factory.overWriteSetter(Text, 'height', getNumberOrAutoValidator());
|
||||
|
||||
|
||||
/**
|
||||
* get/set direction
|
||||
* @name Konva.Text#direction
|
||||
|
||||
@@ -42,6 +42,12 @@ export interface TransformerConfig extends ContainerConfig {
|
||||
boundBoxFunc?: (oldBox: Box, newBox: Box) => Box;
|
||||
useSingleNodeRotation?: boolean;
|
||||
shouldOverdrawWholeArea?: boolean;
|
||||
anchorDragBoundFunc?: (
|
||||
oldPos: Vector2d,
|
||||
newPos: Vector2d,
|
||||
evt: any
|
||||
) => Vector2d;
|
||||
anchorStyleFunc?: (anchor: Rect) => void;
|
||||
}
|
||||
|
||||
var EVENTS_NAME = 'tr-konva';
|
||||
@@ -197,6 +203,7 @@ function getSnap(snaps: Array<number>, newRotationRad: number, tol: number) {
|
||||
return snapped;
|
||||
}
|
||||
|
||||
let activeTransformersCount = 0;
|
||||
/**
|
||||
* Transformer constructor. Transformer is a special type of group that allow you transform Konva
|
||||
* primitives and shapes. Transforming tool is not changing `width` and `height` properties of nodes
|
||||
@@ -238,7 +245,6 @@ function getSnap(snaps: Array<number>, newRotationRad: number, tol: number) {
|
||||
* });
|
||||
* layer.add(transformer);
|
||||
*/
|
||||
|
||||
export class Transformer extends Group {
|
||||
_nodes: Array<Node>;
|
||||
_movingAnchorName: string | null = null;
|
||||
@@ -248,6 +254,10 @@ export class Transformer extends Group {
|
||||
cos: number;
|
||||
_cursorChange: boolean;
|
||||
|
||||
static isTransforming = () => {
|
||||
return activeTransformersCount > 0;
|
||||
};
|
||||
|
||||
constructor(config?: TransformerConfig) {
|
||||
// call super constructor
|
||||
super(config);
|
||||
@@ -660,6 +670,11 @@ export class Transformer extends Group {
|
||||
});
|
||||
}
|
||||
_handleMouseDown(e) {
|
||||
// do nothing if we already transforming
|
||||
// that is possible to trigger with multitouch
|
||||
if (this._transforming) {
|
||||
return;
|
||||
}
|
||||
this._movingAnchorName = e.target.name().split(' ')[0];
|
||||
|
||||
var attrs = this._getNodeRect();
|
||||
@@ -684,6 +699,7 @@ export class Transformer extends Group {
|
||||
x: pos.x - ap.x,
|
||||
y: pos.y - ap.y,
|
||||
};
|
||||
activeTransformersCount++;
|
||||
this._fire('transformstart', { evt: e.evt, target: this.getNode() });
|
||||
this._nodes.forEach((target) => {
|
||||
target._fire('transformstart', { evt: e.evt, target });
|
||||
@@ -949,11 +965,16 @@ export class Transformer extends Group {
|
||||
window.removeEventListener('touchend', this._handleMouseUp, true);
|
||||
}
|
||||
var node = this.getNode();
|
||||
activeTransformersCount--;
|
||||
this._fire('transformend', { evt: e, target: node });
|
||||
// redraw layer to restore hit graph
|
||||
this.getLayer()?.batchDraw();
|
||||
|
||||
if (node) {
|
||||
this._nodes.forEach((target) => {
|
||||
target._fire('transformend', { evt: e, target });
|
||||
// redraw layer to restore hit graph
|
||||
target.getLayer()?.batchDraw();
|
||||
});
|
||||
}
|
||||
this._movingAnchorName = null;
|
||||
@@ -1103,11 +1124,14 @@ export class Transformer extends Group {
|
||||
|
||||
const attrs = newLocalTransform.decompose();
|
||||
node.setAttrs(attrs);
|
||||
this._fire('transform', { evt: evt, target: node });
|
||||
node._fire('transform', { evt: evt, target: node });
|
||||
node.getLayer()?.batchDraw();
|
||||
});
|
||||
this.rotation(Util._getRotation(newAttrs.rotation));
|
||||
// trigger transform event AFTER we update rotation
|
||||
this._nodes.forEach((node) => {
|
||||
this._fire('transform', { evt: evt, target: node });
|
||||
node._fire('transform', { evt: evt, target: node });
|
||||
});
|
||||
this._resetTransformCache();
|
||||
this.update();
|
||||
this.getLayer()!.batchDraw();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { assert } from 'chai';
|
||||
|
||||
import { addStage, Konva, loadImage } from '../unit/test-utils';
|
||||
import { cloneAndCompareLayer } from '../unit/test-utils';
|
||||
|
||||
describe('Pixelate', function () {
|
||||
// ======================================================
|
||||
@@ -42,4 +41,25 @@ describe('Pixelate', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('make sure we have no extra transparent pixels', function (done) {
|
||||
var stage = addStage();
|
||||
var layer = new Konva.Layer();
|
||||
stage.add(layer);
|
||||
|
||||
Konva.Image.fromURL(
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGUAAABmCAYAAADS6F9hAAAAAXNSR0IArs4c6QAAAXJJREFUeF7t1cEJADAMw8B2/6Fd6BT3UCYQEiZ3205HGbhFoXp8mKJ4TYoCNilKUUQDIFM/pSigARCppRQFNAAitZSigAZApJZSFNAAiNRSigIaAJFaSlFAAyBSSykKaABEailFAQ2ASC2lKKABEKmlFAU0ACK1lKKABkCkllIU0ACI1FKKAhoAkVpKUUADIFJLKQpoAERqKUUBDYBILaUooAEQqaUUBTQAIrWUooAGQKSWUhTQAIjUUooCGgCRWkpRQAMgUkspCmgARGopRQENgEgtpSigARCppRQFNAAitZSigAZApJZSFNAAiNRSigIaAJFaSlFAAyBSSykKaABEailFAQ2ASC2lKKABEKmlFAU0ACK1lKKABkCkllIU0ACI1FKKAhoAkVpKUUADIFJLKQpoAERqKUUBDYBILaUooAEQqaUUBTQAIrWUooAGQKSWUhTQAIjUUooCGgCRWkpRQAMgUkspCmgARGopRQENgEgPgGOW3jCsp3sAAAAASUVORK5CYII=',
|
||||
function (image) {
|
||||
layer.add(image);
|
||||
|
||||
image.cache();
|
||||
image.filters([Konva.Filters.Pixelate]);
|
||||
image.pixelSize(4);
|
||||
layer.draw();
|
||||
cloneAndCompareLayer(layer);
|
||||
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -30,45 +30,42 @@
|
||||
<script type="module">
|
||||
import Konva from '../src/index.ts';
|
||||
|
||||
var stageWidth = window.innerWidth;
|
||||
var stageHeight = window.innerHeight;
|
||||
|
||||
Konva._fixTextRendering = true;
|
||||
|
||||
var stage = new Konva.Stage({
|
||||
container: 'container',
|
||||
width: window.innerHeight,
|
||||
height: window.innerHeight,
|
||||
width: stageWidth,
|
||||
height: stageHeight,
|
||||
});
|
||||
|
||||
var layer = new Konva.Layer();
|
||||
stage.add(layer);
|
||||
|
||||
const circle = new Konva.Circle({
|
||||
x: 100,
|
||||
y: 150,
|
||||
radius: 50,
|
||||
var text = new Konva.Text({
|
||||
x: 10,
|
||||
y: 10,
|
||||
text: 'Simple Text',
|
||||
fontSize: 100,
|
||||
fontFamily: 'Calibri',
|
||||
fill: 'green',
|
||||
textDecoration: 'underline line-through',
|
||||
draggable: true,
|
||||
fillLinearGradientStartPoint: { x: -50, y: -50 },
|
||||
fillLinearGradientEndPoint: { x: 50, y: 50 },
|
||||
fillLinearGradientColorStops: [0, 'red', 1, 'yellow'],
|
||||
});
|
||||
layer.add(text);
|
||||
|
||||
layer.add(circle);
|
||||
|
||||
const tr = new Konva.Transformer({
|
||||
nodes: [circle],
|
||||
flipEnabled: false,
|
||||
var tr = new Konva.Transformer({
|
||||
nodes: [text],
|
||||
});
|
||||
layer.add(tr);
|
||||
|
||||
const dot = new Konva.Circle({
|
||||
x: 100,
|
||||
y: 100,
|
||||
radius: 2,
|
||||
fill: 'blue',
|
||||
tr.on('transformstart', () => {
|
||||
console.log('transform start');
|
||||
});
|
||||
|
||||
layer.add(dot);
|
||||
|
||||
circle.on('transform', () => {
|
||||
dot.x(circle.x());
|
||||
dot.y(circle.y() - circle.radius() * circle.scaleY() - 50);
|
||||
tr.on('transformend', () => {
|
||||
console.log('transform end');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -22,6 +22,7 @@ describe('Context', function () {
|
||||
'arc',
|
||||
'arcTo',
|
||||
'rect',
|
||||
'roundRect',
|
||||
'ellipse',
|
||||
'fill',
|
||||
'stroke',
|
||||
|
||||
@@ -6,6 +6,9 @@ import {
|
||||
simulateMouseDown,
|
||||
simulateMouseMove,
|
||||
simulateMouseUp,
|
||||
simulatePointerDown,
|
||||
simulatePointerMove,
|
||||
simulatePointerUp,
|
||||
} from './test-utils';
|
||||
|
||||
describe('DragAndDropEvents', function () {
|
||||
@@ -219,10 +222,6 @@ describe('DragAndDropEvents', function () {
|
||||
clicked = true;
|
||||
});
|
||||
|
||||
circle.on('dblclick', function () {
|
||||
//console.log('dblclick');
|
||||
});
|
||||
|
||||
simulateMouseDown(stage, {
|
||||
x: 40,
|
||||
y: 40,
|
||||
@@ -245,6 +244,58 @@ describe('DragAndDropEvents', function () {
|
||||
}, 20);
|
||||
});
|
||||
|
||||
// TODO: how to solve it?
|
||||
// hint: every shape has pointerId that indicates which pointer is dragging it
|
||||
// but "pointer" event and mouse event has different pointerId
|
||||
// so we need to find a way to match them
|
||||
// should we save several pointers per shape?
|
||||
// doesn't sound good
|
||||
// switch to pointer only event handling?
|
||||
it.skip('click should not occur after drag and drop', function (done) {
|
||||
var stage = addStage();
|
||||
var layer = new Konva.Layer();
|
||||
|
||||
var circle = new Konva.Circle({
|
||||
x: 40,
|
||||
y: 40,
|
||||
radius: 20,
|
||||
strokeWidth: 4,
|
||||
fill: 'green',
|
||||
stroke: 'black',
|
||||
draggable: true,
|
||||
});
|
||||
|
||||
layer.add(circle);
|
||||
stage.add(layer);
|
||||
|
||||
var clicked = false;
|
||||
|
||||
stage.on('pointerclick', function () {
|
||||
clicked = true;
|
||||
});
|
||||
|
||||
simulatePointerDown(stage, {
|
||||
x: 40,
|
||||
y: 40,
|
||||
});
|
||||
|
||||
setTimeout(function () {
|
||||
simulatePointerMove(stage, {
|
||||
x: 100,
|
||||
y: 100,
|
||||
});
|
||||
|
||||
simulatePointerUp(stage, {
|
||||
x: 100,
|
||||
y: 100,
|
||||
});
|
||||
|
||||
assert(!clicked, 'click event should not have been fired');
|
||||
|
||||
done();
|
||||
}, 20);
|
||||
});
|
||||
|
||||
// ======================================================
|
||||
it('drag and drop distance', function (done) {
|
||||
var stage = addStage();
|
||||
|
||||
@@ -55,15 +55,43 @@ describe('Group', function () {
|
||||
var path = new Konva.Group({
|
||||
width: 100,
|
||||
height: 100,
|
||||
clipFunc: () => [new Path2D('M0 0v50h50Z')]
|
||||
clipFunc: () => [new Path2D('M0 0v50h50Z')],
|
||||
});
|
||||
|
||||
layer.add(path);
|
||||
stage.add(layer);
|
||||
|
||||
const trace = layer.getContext().getTrace()
|
||||
const trace = layer.getContext().getTrace();
|
||||
|
||||
assert.equal(trace, 'clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);beginPath();clip([object Path2D]);transform(1,0,0,1,0,0);restore();');
|
||||
assert.equal(
|
||||
trace,
|
||||
'clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);beginPath();clip([object Path2D]);transform(1,0,0,1,0,0);restore();'
|
||||
);
|
||||
});
|
||||
|
||||
it('clip group with by zero size', function () {
|
||||
var stage = addStage();
|
||||
|
||||
var layer = new Konva.Layer();
|
||||
|
||||
var group = new Konva.Group({
|
||||
width: 100,
|
||||
height: 100,
|
||||
clipWidth: 0,
|
||||
clipHeight: 0,
|
||||
});
|
||||
|
||||
layer.add(group);
|
||||
stage.add(layer);
|
||||
|
||||
const trace = layer.getContext().getTrace();
|
||||
|
||||
console.log(trace);
|
||||
|
||||
assert.equal(
|
||||
trace,
|
||||
'clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);beginPath();rect(0,0,0,0);clip();transform(1,0,0,1,0,0);restore();'
|
||||
);
|
||||
});
|
||||
|
||||
it('clip group with a Path2D and clipRule', function () {
|
||||
@@ -80,8 +108,11 @@ describe('Group', function () {
|
||||
layer.add(path);
|
||||
stage.add(layer);
|
||||
|
||||
const trace = layer.getContext().getTrace()
|
||||
const trace = layer.getContext().getTrace();
|
||||
|
||||
assert.equal(trace, 'clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);beginPath();clip([object Path2D],evenodd);transform(1,0,0,1,0,0);restore();');
|
||||
assert.equal(
|
||||
trace,
|
||||
'clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);beginPath();clip([object Path2D],evenodd);transform(1,0,0,1,0,0);restore();'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -413,4 +413,39 @@ describe('Image', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('corner radius with shadow', function (done) {
|
||||
// that will trigger buffer canvas
|
||||
loadImage('darth-vader.jpg', (imageObj) => {
|
||||
var stage = addStage();
|
||||
|
||||
var layer = new Konva.Layer();
|
||||
var darth = new Konva.Image({
|
||||
x: 20,
|
||||
y: 20,
|
||||
image: imageObj,
|
||||
cornerRadius: 10,
|
||||
draggable: true,
|
||||
stroke: 'red',
|
||||
strokeWidth: 100,
|
||||
strokeEnabled: false,
|
||||
shadowColor: 'black',
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 10,
|
||||
shadowOffsetY: 10,
|
||||
scaleX: 0.5,
|
||||
scaleY: 0.5,
|
||||
});
|
||||
|
||||
layer.add(darth);
|
||||
stage.add(layer);
|
||||
|
||||
assert.equal(
|
||||
layer.getContext().getTrace(true),
|
||||
'clearRect();save();shadowColor;shadowBlur;shadowOffsetX;shadowOffsetY;drawImage();restore();'
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2377,4 +2377,67 @@ describe('MouseEvents', function () {
|
||||
});
|
||||
assert.deepEqual(stage.getPointerPosition(), { x: 60, y: 60 });
|
||||
});
|
||||
|
||||
it('mousedown on empty then mouseup on shape', function () {
|
||||
if (isNode) {
|
||||
return;
|
||||
}
|
||||
var stage = addStage();
|
||||
var layer = new Konva.Layer();
|
||||
stage.add(layer);
|
||||
|
||||
stage.on('mousedown mousemove mouseup click', function (e) {
|
||||
console.log('state', e.type);
|
||||
});
|
||||
|
||||
var rect = new Konva.Rect({
|
||||
width: 50,
|
||||
height: 50,
|
||||
fill: 'red',
|
||||
draggable: true,
|
||||
});
|
||||
layer.add(rect);
|
||||
|
||||
layer.draw();
|
||||
|
||||
var clicks = 0;
|
||||
rect.on('click', function () {
|
||||
console.log('click');
|
||||
clicks += 1;
|
||||
if (clicks === 2) {
|
||||
debugger;
|
||||
}
|
||||
});
|
||||
|
||||
simulateMouseDown(stage, {
|
||||
x: 40,
|
||||
y: 40,
|
||||
});
|
||||
|
||||
simulateMouseUp(stage, {
|
||||
x: 40,
|
||||
y: 40,
|
||||
});
|
||||
|
||||
// trigger click
|
||||
assert.equal(clicks, 1, 'clicks not triggered');
|
||||
|
||||
// mousedown outside
|
||||
simulateMouseDown(stage, {
|
||||
x: 60,
|
||||
y: 6,
|
||||
});
|
||||
// move into rect
|
||||
simulateMouseMove(stage, {
|
||||
x: 40,
|
||||
y: 40,
|
||||
});
|
||||
// mouseup inside rect
|
||||
simulateMouseUp(stage, {
|
||||
x: 40,
|
||||
y: 40,
|
||||
});
|
||||
// it shouldn't trigger the click event!!!
|
||||
assert.equal(clicks, 1, 'clicks not triggered');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1469,7 +1469,7 @@ describe('Caching', function () {
|
||||
layer.draw();
|
||||
assert.equal(
|
||||
circle._cache.get('canvas').filter.width,
|
||||
21 * circle._cache.get('canvas').filter.pixelRatio
|
||||
20 * circle._cache.get('canvas').filter.pixelRatio
|
||||
);
|
||||
circle.filters([]);
|
||||
// TODO: should we clear cache canvas?
|
||||
|
||||
@@ -1134,11 +1134,11 @@ describe('Path', function () {
|
||||
|
||||
assert.deepEqual(points, [
|
||||
{ x: 300, y: 10 },
|
||||
{ x: 290.2871413779118, y: 27.48314552325543 },
|
||||
{ x: 280.57428275582356, y: 44.96629104651086 },
|
||||
{ x: 270.86142413373534, y: 62.4494365697663 },
|
||||
{ x: 261.1485655116471, y: 79.93258209302172 },
|
||||
{ x: 251.43570688955887, y: 97.41572761627717 },
|
||||
{ x: 290.28714137642737, y: 27.483145522430753 },
|
||||
{ x: 280.57428275285474, y: 44.96629104486151 },
|
||||
{ x: 270.86142412928206, y: 62.44943656729226 },
|
||||
{ x: 261.1485655057094, y: 79.93258208972301 },
|
||||
{ x: 251.4357068821368, y: 97.41572761215377 },
|
||||
{ x: 230.89220826660141, y: 87.23996356219386 },
|
||||
{ x: 207.0639321224534, y: 74.08466390481559 },
|
||||
{ x: 182.87529785963875, y: 63.52674972743341 },
|
||||
@@ -1168,6 +1168,47 @@ describe('Path', function () {
|
||||
stage.add(layer);
|
||||
});
|
||||
|
||||
it('get point at path with float attrs', function () {
|
||||
var stage = addStage();
|
||||
var layer = new Konva.Layer();
|
||||
|
||||
const data =
|
||||
'M419.0000314094981 342.88624187900973 L419.00003140949804 577.0038889378335 L465.014001082264 577.0038889378336 Z';
|
||||
var path = new Konva.Path({
|
||||
stroke: 'red',
|
||||
strokeWidth: 3,
|
||||
data,
|
||||
});
|
||||
layer.add(path);
|
||||
if (isBrowser) {
|
||||
const SVGPath = document.createElementNS(
|
||||
'http://www.w3.org/2000/svg',
|
||||
'path'
|
||||
) as SVGPathElement;
|
||||
SVGPath.setAttribute('d', data);
|
||||
for (var i = 0; i < path.getLength(); i += 1) {
|
||||
var p = path.getPointAtLength(i);
|
||||
var circle = new Konva.Circle({
|
||||
x: p.x,
|
||||
y: p.y,
|
||||
radius: 2,
|
||||
fill: 'black',
|
||||
stroke: 'black',
|
||||
});
|
||||
layer.add(circle);
|
||||
const position = SVGPath.getPointAtLength(i);
|
||||
assert(
|
||||
Math.abs(p.x - position.x) <= 1,
|
||||
'error for x should be smaller than 10% for i = ' + i
|
||||
);
|
||||
assert(
|
||||
Math.abs(p.y - position.y) <= 1,
|
||||
'error for y should be smaller than 10% for i = ' + i
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('get point at path - bezier', function () {
|
||||
var stage = addStage();
|
||||
var layer = new Konva.Layer();
|
||||
@@ -1618,8 +1659,11 @@ describe('Path', function () {
|
||||
layer.add(path);
|
||||
stage.add(layer);
|
||||
|
||||
const trace = layer.getContext().getTrace()
|
||||
const trace = layer.getContext().getTrace();
|
||||
|
||||
assert.equal(trace, 'clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);beginPath();moveTo(200,100);lineTo(300,100);lineTo(300,150);closePath();fillStyle=#ccc;fill(evenodd);restore();');
|
||||
assert.equal(
|
||||
trace,
|
||||
'clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);beginPath();moveTo(200,100);lineTo(300,100);lineTo(300,150);closePath();fillStyle=#ccc;fill(evenodd);restore();'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
compareLayers,
|
||||
loadImage,
|
||||
Konva,
|
||||
compareCanvases,
|
||||
} from './test-utils';
|
||||
|
||||
describe('Shape', function () {
|
||||
@@ -1479,6 +1480,76 @@ describe('Shape', function () {
|
||||
}
|
||||
});
|
||||
|
||||
it('export when buffer canvas is used should handle scaling correctly', async function () {
|
||||
var stage = addStage();
|
||||
|
||||
var layer = new Konva.Layer();
|
||||
stage.add(layer);
|
||||
|
||||
var group = new Konva.Group();
|
||||
layer.add(group);
|
||||
|
||||
var text = new Konva.Text({
|
||||
text: 'hello',
|
||||
fontSize: 300,
|
||||
fill: 'green',
|
||||
shadowColor: 'black',
|
||||
});
|
||||
group.add(text);
|
||||
|
||||
const canvas1 = group.toCanvas({
|
||||
x: group.x(),
|
||||
y: group.y(),
|
||||
width: text.width(),
|
||||
height: text.height(),
|
||||
});
|
||||
text.stroke('transparent');
|
||||
const canvas2 = group.toCanvas({
|
||||
x: group.x(),
|
||||
y: group.y(),
|
||||
width: text.width(),
|
||||
height: text.height(),
|
||||
});
|
||||
|
||||
compareCanvases(canvas2, canvas1, 255, 10);
|
||||
});
|
||||
|
||||
it('export when buffer canvas is used should handle scaling correctly another time', async function () {
|
||||
var stage = addStage();
|
||||
|
||||
var layer = new Konva.Layer();
|
||||
stage.add(layer);
|
||||
|
||||
var group = new Konva.Group({
|
||||
x: 400,
|
||||
});
|
||||
layer.add(group);
|
||||
|
||||
var text = new Konva.Text({
|
||||
text: 'hello',
|
||||
fontSize: 300,
|
||||
fill: 'green',
|
||||
shadowColor: 'black',
|
||||
});
|
||||
group.add(text);
|
||||
|
||||
const canvas1 = group.toCanvas({
|
||||
x: group.x(),
|
||||
y: group.y(),
|
||||
width: text.width(),
|
||||
height: text.height(),
|
||||
});
|
||||
text.stroke('transparent');
|
||||
const canvas2 = group.toCanvas({
|
||||
x: group.x(),
|
||||
y: group.y(),
|
||||
width: text.width(),
|
||||
height: text.height(),
|
||||
});
|
||||
|
||||
compareCanvases(canvas2, canvas1, 240, 110);
|
||||
});
|
||||
|
||||
// ======================================================
|
||||
it('optional disable shadow for stroke', function () {
|
||||
var stage = addStage();
|
||||
@@ -1582,6 +1653,35 @@ describe('Shape', function () {
|
||||
assert.equal(rect.height, 100, 'should not effect width');
|
||||
});
|
||||
|
||||
it('getClientRect should not use cached values', function () {
|
||||
var stage = addStage();
|
||||
var layer = new Konva.Layer();
|
||||
|
||||
var shape = new Konva.Rect({
|
||||
x: 100,
|
||||
y: 100,
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: 'green',
|
||||
stroke: 'black',
|
||||
strokeWidth: 4,
|
||||
strokeEnabled: false,
|
||||
shadowOffsetX: 10,
|
||||
shadowEnabled: false,
|
||||
});
|
||||
|
||||
layer.add(shape);
|
||||
stage.add(layer);
|
||||
|
||||
layer.cache();
|
||||
|
||||
layer.scaleX(2);
|
||||
|
||||
const rect = shape.getClientRect();
|
||||
|
||||
assert.equal(rect.x, 200);
|
||||
});
|
||||
|
||||
it('getClientRect for shape in transformed parent', function () {
|
||||
var stage = addStage();
|
||||
var layer = new Konva.Layer();
|
||||
@@ -2203,4 +2303,36 @@ describe('Shape', function () {
|
||||
assert.equal(callCount, 0);
|
||||
Konva.Util.warn = oldWarn;
|
||||
});
|
||||
|
||||
it('fill rule on hit graph', function () {
|
||||
var stage = addStage();
|
||||
|
||||
var layer = new Konva.Layer();
|
||||
stage.add(layer);
|
||||
|
||||
var mask = new Konva.Shape({
|
||||
sceneFunc: function (ctx, shape) {
|
||||
ctx.beginPath();
|
||||
ctx.rect(0, 0, 500, 500);
|
||||
ctx.rect(100, 100, 100, 100);
|
||||
ctx.closePath();
|
||||
ctx.fillShape(shape);
|
||||
},
|
||||
draggable: true,
|
||||
fill: 'red',
|
||||
fillRule: 'evenodd',
|
||||
});
|
||||
|
||||
layer.add(mask);
|
||||
layer.draw();
|
||||
const trace = layer.getContext().getTrace();
|
||||
|
||||
assert.equal(
|
||||
trace,
|
||||
'clearRect(0,0,578,200);clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);beginPath();rect(0,0,500,500);rect(100,100,100,100);closePath();fillStyle=red;fill(evenodd);restore();'
|
||||
);
|
||||
|
||||
const hitShape = layer.getIntersection({ x: 150, y: 150 });
|
||||
assert.equal(hitShape, null);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1654,7 +1654,7 @@ describe('Text', function () {
|
||||
|
||||
assert.equal(layer.getContext().getTrace(), trace);
|
||||
});
|
||||
|
||||
|
||||
it('sets ltr text direction', function () {
|
||||
var stage = addStage();
|
||||
var layer = new Konva.Layer();
|
||||
@@ -1673,9 +1673,8 @@ describe('Text', function () {
|
||||
|
||||
assert.equal(layer.getContext().getTrace(), trace);
|
||||
});
|
||||
|
||||
|
||||
it('sets rtl text direction', function () {
|
||||
|
||||
it('sets rtl text direction', function () {
|
||||
var stage = addStage();
|
||||
var layer = new Konva.Layer();
|
||||
|
||||
@@ -1693,7 +1692,7 @@ describe('Text', function () {
|
||||
|
||||
assert.equal(layer.getContext().getTrace(), trace);
|
||||
});
|
||||
|
||||
|
||||
it('sets rtl text direction with letterSpacing', function () {
|
||||
var stage = addStage();
|
||||
var layer = new Konva.Layer();
|
||||
@@ -1713,4 +1712,22 @@ describe('Text', function () {
|
||||
|
||||
assert.equal(layer.getContext().getTrace(), trace);
|
||||
});
|
||||
|
||||
it('try fixed render', () => {
|
||||
Konva._fixTextRendering = true;
|
||||
var stage = addStage();
|
||||
var layer = new Konva.Layer();
|
||||
|
||||
stage.add(layer);
|
||||
var text = new Konva.Text({ text: 'hello', fontSize: 100 });
|
||||
|
||||
layer.add(text);
|
||||
layer.draw();
|
||||
Konva._fixTextRendering = false;
|
||||
|
||||
const trace =
|
||||
'clearRect(0,0,578,200);clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);font=normal normal 100px Arial;textBaseline=alphabetic;textAlign=left;translate(0,0);save();fillStyle=black;fillText(hello,0,85);restore();restore();';
|
||||
|
||||
assert.equal(layer.getContext().getTrace(), trace);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -460,11 +460,11 @@ describe('TouchEvents', function () {
|
||||
);
|
||||
assert.equal(touchEnd, 1);
|
||||
assert.equal(stageTouchEnd, 1);
|
||||
assert.equal(stageTap, 2, 'one tap should be fired');
|
||||
assert.equal(stageTap, 1, 'one tap should be fired');
|
||||
|
||||
assert.equal(
|
||||
stageEventStack.join(' '),
|
||||
'touchstart touchstart touchstart touchend tap tap',
|
||||
'touchstart touchstart touchstart touchend tap',
|
||||
'should fire tap after touchend'
|
||||
);
|
||||
|
||||
@@ -481,7 +481,7 @@ describe('TouchEvents', function () {
|
||||
assert.equal(touchEnd, 2);
|
||||
assert.equal(touchEnd2, 1);
|
||||
assert.equal(stageTouchEnd, 3);
|
||||
assert.equal(stageTap, 2, 'still one tap should be fired');
|
||||
assert.equal(stageTap, 1, 'still one tap should be fired');
|
||||
// Don't need to check event stack here, the pointers moved so no tap is fired
|
||||
});
|
||||
|
||||
|
||||
@@ -4929,4 +4929,163 @@ describe('Transformer', function () {
|
||||
assertAlmostEqual(rect.scaleX(), 1);
|
||||
assertAlmostEqual(rect.scaleY(), 1);
|
||||
});
|
||||
|
||||
it('should be able to prevent rotation in transform event', function () {
|
||||
var stage = addStage();
|
||||
var layer = new Konva.Layer();
|
||||
stage.add(layer);
|
||||
|
||||
var rect = new Konva.Rect({
|
||||
x: 55,
|
||||
y: 55,
|
||||
draggable: true,
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: 'yellow',
|
||||
});
|
||||
layer.add(rect);
|
||||
|
||||
var tr = new Konva.Transformer({
|
||||
nodes: [rect],
|
||||
});
|
||||
layer.add(tr);
|
||||
layer.draw();
|
||||
|
||||
tr.on('transform', function (e) {
|
||||
tr.rotation(0);
|
||||
});
|
||||
|
||||
simulateMouseDown(tr, {
|
||||
x: 100,
|
||||
y: 2,
|
||||
});
|
||||
simulateMouseMove(tr, {
|
||||
x: 110,
|
||||
y: 2,
|
||||
});
|
||||
assert.equal(tr.rotation(), 0);
|
||||
simulateMouseUp(tr, { x: 110, y: 2 });
|
||||
});
|
||||
|
||||
it('skip render on hit graph while transforming', function (done) {
|
||||
var stage = addStage();
|
||||
var layer = new Konva.Layer();
|
||||
stage.add(layer);
|
||||
|
||||
var rect = new Konva.Rect({
|
||||
x: 55,
|
||||
y: 55,
|
||||
draggable: true,
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: 'yellow',
|
||||
});
|
||||
layer.add(rect);
|
||||
|
||||
var tr = new Konva.Transformer({
|
||||
nodes: [rect],
|
||||
});
|
||||
layer.add(tr);
|
||||
layer.draw();
|
||||
|
||||
simulateMouseDown(tr, {
|
||||
x: 100,
|
||||
y: 2,
|
||||
});
|
||||
simulateMouseMove(tr, {
|
||||
x: 110,
|
||||
y: 2,
|
||||
});
|
||||
let shape = layer.getIntersection({
|
||||
x: 100,
|
||||
y: 100,
|
||||
});
|
||||
assert.equal(shape, null);
|
||||
simulateMouseUp(tr, { x: 110, y: 2 });
|
||||
layer.draw();
|
||||
shape = layer.getIntersection({
|
||||
x: 100,
|
||||
y: 100,
|
||||
});
|
||||
assert.equal(shape, rect);
|
||||
// reset position
|
||||
rect.setAttrs({
|
||||
x: 50,
|
||||
y: 50,
|
||||
draggable: true,
|
||||
width: 100,
|
||||
height: 100,
|
||||
});
|
||||
|
||||
tr.nodes([rect]);
|
||||
layer.draw();
|
||||
// now check if graph is visible back when we moved a bit
|
||||
simulateMouseDown(tr, {
|
||||
x: 100,
|
||||
y: 2,
|
||||
});
|
||||
simulateMouseMove(tr, {
|
||||
x: 110,
|
||||
y: 2,
|
||||
});
|
||||
setTimeout(() => {
|
||||
shape = layer.getIntersection({
|
||||
x: 100,
|
||||
y: 100,
|
||||
});
|
||||
assert.equal(shape, null);
|
||||
simulateMouseUp(tr, { x: 110, y: 2 });
|
||||
setTimeout(() => {
|
||||
shape = layer.getIntersection({
|
||||
x: 100,
|
||||
y: 100,
|
||||
});
|
||||
assert.equal(shape, rect);
|
||||
done();
|
||||
}, 100);
|
||||
}, 100);
|
||||
});
|
||||
|
||||
it('enable hit graph back on transformer destroy', function (done) {
|
||||
var stage = addStage();
|
||||
var layer = new Konva.Layer();
|
||||
stage.add(layer);
|
||||
|
||||
var rect = new Konva.Rect({
|
||||
x: 55,
|
||||
y: 55,
|
||||
draggable: true,
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: 'yellow',
|
||||
});
|
||||
layer.add(rect);
|
||||
|
||||
var tr = new Konva.Transformer({
|
||||
nodes: [rect],
|
||||
});
|
||||
layer.add(tr);
|
||||
layer.draw();
|
||||
|
||||
// now check if graph is visible back when we moved a bit
|
||||
simulateMouseDown(tr, {
|
||||
x: 100,
|
||||
y: 2,
|
||||
});
|
||||
simulateMouseMove(tr, {
|
||||
x: 110,
|
||||
y: 2,
|
||||
});
|
||||
setTimeout(() => {
|
||||
tr.destroy();
|
||||
setTimeout(() => {
|
||||
var shape = layer.getIntersection({
|
||||
x: 100,
|
||||
y: 100,
|
||||
});
|
||||
assert.equal(shape, rect);
|
||||
done();
|
||||
}, 100);
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
"noImplicitAny": false,
|
||||
"noImplicitThis": false,
|
||||
"useUnknownInCatchVariables": false,
|
||||
"skipLibCheck": true,
|
||||
// probably we would never enable this one
|
||||
// because it's too strict, konva generates many functions on the runtime
|
||||
"strictPropertyInitialization": false,
|
||||
|
||||
},
|
||||
"include": ["./src/**/*.ts"]
|
||||
"include": ["./src/**/*.ts"],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user