mirror of
https://github.com/konvajs/konva.git
synced 2025-06-28 05:10:26 +08:00
Merge branch 'konvajs:master' into master
This commit is contained in:
commit
e793c8270a
@ -3,6 +3,11 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## 9.3.21 (not released)
|
||||||
|
|
||||||
|
- Fixed memory leaks on Tween destroy
|
||||||
|
- Fixed incorrect export of stage/layer when internal nodes used buffer canvas for rendering
|
||||||
|
|
||||||
## 9.3.20 (2025-03-20)
|
## 9.3.20 (2025-03-20)
|
||||||
|
|
||||||
- Fix text rendering when ellipses are used
|
- Fix text rendering when ellipses are used
|
||||||
|
@ -120,8 +120,6 @@ export abstract class Container<
|
|||||||
* layer.add(shape1, shape2, shape3);
|
* layer.add(shape1, shape2, shape3);
|
||||||
* // empty arrays are accepted, though each individual child must be defined
|
* // empty arrays are accepted, though each individual child must be defined
|
||||||
* layer.add(...shapes);
|
* layer.add(...shapes);
|
||||||
* // remember to redraw layer if you changed something
|
|
||||||
* layer.draw();
|
|
||||||
*/
|
*/
|
||||||
add(...children: ChildType[]) {
|
add(...children: ChildType[]) {
|
||||||
if (children.length === 0) {
|
if (children.length === 0) {
|
||||||
|
@ -385,7 +385,7 @@ export class Layer extends Container<Group | Shape> {
|
|||||||
// empty pixel
|
// empty pixel
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
drawScene(can?: SceneCanvas, top?: Node) {
|
drawScene(can?: SceneCanvas, top?: Node, bufferCanvas?: SceneCanvas) {
|
||||||
const layer = this.getLayer(),
|
const layer = this.getLayer(),
|
||||||
canvas = can || (layer && layer.getCanvas());
|
canvas = can || (layer && layer.getCanvas());
|
||||||
|
|
||||||
@ -397,7 +397,7 @@ export class Layer extends Container<Group | Shape> {
|
|||||||
canvas.getContext().clear();
|
canvas.getContext().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
Container.prototype.drawScene.call(this, canvas, top);
|
Container.prototype.drawScene.call(this, canvas, top, bufferCanvas);
|
||||||
|
|
||||||
this._fire(DRAW, {
|
this._fire(DRAW, {
|
||||||
node: this,
|
node: this,
|
||||||
|
33
src/Node.ts
33
src/Node.ts
@ -693,39 +693,32 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
evtStr: K,
|
evtStr: K,
|
||||||
handler: KonvaEventListener<this, NodeEventMap[K]>
|
handler: KonvaEventListener<this, NodeEventMap[K]>
|
||||||
) {
|
) {
|
||||||
this._cache && this._cache.delete(ALL_LISTENERS);
|
if (this._cache) {
|
||||||
|
this._cache.delete(ALL_LISTENERS);
|
||||||
|
}
|
||||||
|
|
||||||
if (arguments.length === 3) {
|
if (arguments.length === 3) {
|
||||||
return this._delegate.apply(this, arguments as any);
|
return this._delegate.apply(this, arguments as any);
|
||||||
}
|
}
|
||||||
let events = (evtStr as string).split(SPACE),
|
const events = (evtStr as string).split(SPACE);
|
||||||
len = events.length,
|
|
||||||
n,
|
|
||||||
event,
|
|
||||||
parts,
|
|
||||||
baseEvent,
|
|
||||||
name;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* loop through types and attach event listeners to
|
* loop through types and attach event listeners to
|
||||||
* each one. eg. 'click mouseover.namespace mouseout'
|
* each one. eg. 'click mouseover.namespace mouseout'
|
||||||
* will create three event bindings
|
* will create three event bindings
|
||||||
*/
|
*/
|
||||||
for (n = 0; n < len; n++) {
|
for (let n = 0; n < events.length; n++) {
|
||||||
event = events[n];
|
const event = events[n];
|
||||||
parts = event.split('.');
|
const parts = event.split('.');
|
||||||
baseEvent = parts[0];
|
const baseEvent = parts[0];
|
||||||
name = parts[1] || '';
|
const name = parts[1] || '';
|
||||||
|
|
||||||
// create events array if it doesn't exist
|
// create events array if it doesn't exist
|
||||||
if (!this.eventListeners[baseEvent]) {
|
if (!this.eventListeners[baseEvent]) {
|
||||||
this.eventListeners[baseEvent] = [];
|
this.eventListeners[baseEvent] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.eventListeners[baseEvent].push({
|
this.eventListeners[baseEvent].push({ name , handler });
|
||||||
name: name,
|
|
||||||
handler: handler,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
@ -2208,7 +2201,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
* node.addName('selected');
|
* node.addName('selected');
|
||||||
* node.name(); // return 'red selected'
|
* node.name(); // return 'red selected'
|
||||||
*/
|
*/
|
||||||
addName(name) {
|
addName(name: string) {
|
||||||
if (!this.hasName(name)) {
|
if (!this.hasName(name)) {
|
||||||
const oldName = this.name();
|
const oldName = this.name();
|
||||||
const newName = oldName ? oldName + ' ' + name : name;
|
const newName = oldName ? oldName + ' ' + name : name;
|
||||||
@ -2380,7 +2373,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
|
|
||||||
const topListeners = this._getProtoListeners(eventType);
|
const topListeners = this._getProtoListeners(eventType);
|
||||||
if (topListeners) {
|
if (topListeners) {
|
||||||
for (var i = 0; i < topListeners.length; i++) {
|
for (let i = 0; i < topListeners.length; i++) {
|
||||||
topListeners[i].handler.call(this, evt);
|
topListeners[i].handler.call(this, evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2389,7 +2382,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
// because events can be added/removed while firing
|
// because events can be added/removed while firing
|
||||||
const selfListeners = this.eventListeners[eventType];
|
const selfListeners = this.eventListeners[eventType];
|
||||||
if (selfListeners) {
|
if (selfListeners) {
|
||||||
for (var i = 0; i < selfListeners.length; i++) {
|
for (let i = 0; i < selfListeners.length; i++) {
|
||||||
selfListeners[i].handler.call(this, evt);
|
selfListeners[i].handler.call(this, evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
src/Shape.ts
11
src/Shape.ts
@ -594,13 +594,12 @@ export class Shape<
|
|||||||
// 3 - when node is cached and we need to draw it into layer
|
// 3 - when node is cached and we need to draw it into layer
|
||||||
|
|
||||||
const layer = this.getLayer();
|
const layer = this.getLayer();
|
||||||
let canvas = can || layer!.getCanvas(),
|
const canvas = can || layer!.getCanvas(),
|
||||||
context = canvas.getContext() as SceneContext,
|
context = canvas.getContext() as SceneContext,
|
||||||
cachedCanvas = this._getCanvasCache(),
|
cachedCanvas = this._getCanvasCache(),
|
||||||
drawFunc = this.getSceneFunc(),
|
drawFunc = this.getSceneFunc(),
|
||||||
hasShadow = this.hasShadow(),
|
hasShadow = this.hasShadow();
|
||||||
stage,
|
let stage, bufferContext;
|
||||||
bufferContext;
|
|
||||||
|
|
||||||
const skipBuffer = canvas.isCache;
|
const skipBuffer = canvas.isCache;
|
||||||
const cachingSelf = top === this;
|
const cachingSelf = top === this;
|
||||||
@ -633,7 +632,7 @@ export class Shape<
|
|||||||
bufferContext.save();
|
bufferContext.save();
|
||||||
bufferContext._applyLineJoin(this);
|
bufferContext._applyLineJoin(this);
|
||||||
// layer might be undefined if we are using cache before adding to layer
|
// layer might be undefined if we are using cache before adding to layer
|
||||||
var o = this.getAbsoluteTransform(top).getMatrix();
|
const o = this.getAbsoluteTransform(top).getMatrix();
|
||||||
bufferContext.transform(o[0], o[1], o[2], o[3], o[4], o[5]);
|
bufferContext.transform(o[0], o[1], o[2], o[3], o[4], o[5]);
|
||||||
|
|
||||||
drawFunc.call(this, bufferContext, this);
|
drawFunc.call(this, bufferContext, this);
|
||||||
@ -651,7 +650,7 @@ export class Shape<
|
|||||||
context._applyLineJoin(this);
|
context._applyLineJoin(this);
|
||||||
|
|
||||||
if (!cachingSelf) {
|
if (!cachingSelf) {
|
||||||
var o = this.getAbsoluteTransform(top).getMatrix();
|
const o = this.getAbsoluteTransform(top).getMatrix();
|
||||||
context.transform(o[0], o[1], o[2], o[3], o[4], o[5]);
|
context.transform(o[0], o[1], o[2], o[3], o[4], o[5]);
|
||||||
context._applyOpacity(this);
|
context._applyOpacity(this);
|
||||||
context._applyGlobalCompositeOperation(this);
|
context._applyGlobalCompositeOperation(this);
|
||||||
|
@ -214,11 +214,11 @@ export class Stage extends Container<Layer> {
|
|||||||
*/
|
*/
|
||||||
setContainer(container) {
|
setContainer(container) {
|
||||||
if (typeof container === STRING) {
|
if (typeof container === STRING) {
|
||||||
|
let id;
|
||||||
if (container.charAt(0) === '.') {
|
if (container.charAt(0) === '.') {
|
||||||
const className = container.slice(1);
|
const className = container.slice(1);
|
||||||
container = document.getElementsByClassName(className)[0];
|
container = document.getElementsByClassName(className)[0];
|
||||||
} else {
|
} else {
|
||||||
var id;
|
|
||||||
if (container.charAt(0) !== '#') {
|
if (container.charAt(0) !== '#') {
|
||||||
id = container;
|
id = container;
|
||||||
} else {
|
} else {
|
||||||
|
33
src/Tween.ts
33
src/Tween.ts
@ -200,8 +200,7 @@ export class Tween {
|
|||||||
nodeId = node._id,
|
nodeId = node._id,
|
||||||
easing = config.easing || Easings.Linear,
|
easing = config.easing || Easings.Linear,
|
||||||
yoyo = !!config.yoyo;
|
yoyo = !!config.yoyo;
|
||||||
let duration,
|
let duration, key;
|
||||||
key;
|
|
||||||
|
|
||||||
if (typeof config.duration === 'undefined') {
|
if (typeof config.duration === 'undefined') {
|
||||||
duration = 0.3;
|
duration = 0.3;
|
||||||
@ -268,11 +267,7 @@ export class Tween {
|
|||||||
_addAttr(key, end) {
|
_addAttr(key, end) {
|
||||||
const node = this.node,
|
const node = this.node,
|
||||||
nodeId = node._id;
|
nodeId = node._id;
|
||||||
let diff,
|
let diff, len, trueEnd, trueStart, endRGBA;
|
||||||
len,
|
|
||||||
trueEnd,
|
|
||||||
trueStart,
|
|
||||||
endRGBA;
|
|
||||||
|
|
||||||
// remove conflict from tween map if it exists
|
// remove conflict from tween map if it exists
|
||||||
const tweenId = Tween.tweens[nodeId][key];
|
const tweenId = Tween.tweens[nodeId][key];
|
||||||
@ -352,14 +347,7 @@ export class Tween {
|
|||||||
_tweenFunc(i) {
|
_tweenFunc(i) {
|
||||||
const node = this.node,
|
const node = this.node,
|
||||||
attrs = Tween.attrs[node._id][this._id];
|
attrs = Tween.attrs[node._id][this._id];
|
||||||
let key,
|
let key, attr, start, diff, newVal, n, len, end;
|
||||||
attr,
|
|
||||||
start,
|
|
||||||
diff,
|
|
||||||
newVal,
|
|
||||||
n,
|
|
||||||
len,
|
|
||||||
end;
|
|
||||||
|
|
||||||
for (key in attrs) {
|
for (key in attrs) {
|
||||||
attr = attrs[key];
|
attr = attrs[key];
|
||||||
@ -528,11 +516,26 @@ export class Tween {
|
|||||||
|
|
||||||
this.pause();
|
this.pause();
|
||||||
|
|
||||||
|
// Clean up animation
|
||||||
|
if (this.anim) {
|
||||||
|
this.anim.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up tween entries
|
||||||
for (const key in attrs) {
|
for (const key in attrs) {
|
||||||
delete Tween.tweens[nodeId][key];
|
delete Tween.tweens[nodeId][key];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clean up attrs entry
|
||||||
delete Tween.attrs[nodeId][thisId];
|
delete Tween.attrs[nodeId][thisId];
|
||||||
|
|
||||||
|
// Clean up parent objects if empty
|
||||||
|
if (Object.keys(Tween.tweens[nodeId]).length === 0) {
|
||||||
|
delete Tween.tweens[nodeId];
|
||||||
|
}
|
||||||
|
if (Object.keys(Tween.attrs[nodeId]).length === 0) {
|
||||||
|
delete Tween.attrs[nodeId];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +163,6 @@ export class Image extends Shape<ImageConfig> {
|
|||||||
* Konva.Image.fromURL(imageURL, function(image){
|
* Konva.Image.fromURL(imageURL, function(image){
|
||||||
* // image is Konva.Image instance
|
* // image is Konva.Image instance
|
||||||
* layer.add(image);
|
* layer.add(image);
|
||||||
* layer.draw();
|
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
static fromURL(
|
static fromURL(
|
||||||
|
@ -102,26 +102,24 @@ export class Line<
|
|||||||
}
|
}
|
||||||
|
|
||||||
_sceneFunc(context: Context) {
|
_sceneFunc(context: Context) {
|
||||||
let points = this.points(),
|
const points = this.points(),
|
||||||
length = points.length,
|
length = points.length,
|
||||||
tension = this.tension(),
|
tension = this.tension(),
|
||||||
closed = this.closed(),
|
closed = this.closed(),
|
||||||
bezier = this.bezier(),
|
bezier = this.bezier();
|
||||||
tp,
|
|
||||||
len,
|
|
||||||
n;
|
|
||||||
|
|
||||||
if (!length) {
|
if (!length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let n = 0;
|
||||||
|
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.moveTo(points[0], points[1]);
|
context.moveTo(points[0], points[1]);
|
||||||
|
|
||||||
// tension
|
// tension
|
||||||
if (tension !== 0 && length > 4) {
|
if (tension !== 0 && length > 4) {
|
||||||
tp = this.getTensionPoints();
|
const tp = this.getTensionPoints();
|
||||||
len = tp.length;
|
const len = tp.length;
|
||||||
n = closed ? 0 : 4;
|
n = closed ? 0 : 4;
|
||||||
|
|
||||||
if (!closed) {
|
if (!closed) {
|
||||||
|
@ -171,7 +171,7 @@ function checkDefaultFill(config?: TextConfig) {
|
|||||||
* @param {String} [config.fontVariant] can be normal or small-caps. Default is normal
|
* @param {String} [config.fontVariant] can be normal or small-caps. Default is normal
|
||||||
* @param {String} [config.textDecoration] can be line-through, underline or empty string. Default is empty string.
|
* @param {String} [config.textDecoration] can be line-through, underline or empty string. Default is empty string.
|
||||||
* @param {String} config.text
|
* @param {String} config.text
|
||||||
* @param {String} [config.align] can be left, center, or right
|
* @param {String} [config.align] can be left, center, right or justify
|
||||||
* @param {String} [config.verticalAlign] can be top, middle or bottom
|
* @param {String} [config.verticalAlign] can be top, middle or bottom
|
||||||
* @param {Number} [config.padding]
|
* @param {Number} [config.padding]
|
||||||
* @param {Number} [config.lineHeight] default is 1
|
* @param {Number} [config.lineHeight] default is 1
|
||||||
@ -885,6 +885,8 @@ Factory.addGetterSetter(Text, 'padding', 0, getNumberValidator());
|
|||||||
*
|
*
|
||||||
* // align text to right
|
* // align text to right
|
||||||
* text.align('right');
|
* text.align('right');
|
||||||
|
*
|
||||||
|
* // justify text
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Factory.addGetterSetter(Text, 'align', LEFT);
|
Factory.addGetterSetter(Text, 'align', LEFT);
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
<script type="module">
|
<script type="module">
|
||||||
// CORE
|
// CORE
|
||||||
import './unit/Animation-test.ts';
|
import './unit/Animation-test.ts';
|
||||||
|
import './unit/Tween-test.ts';
|
||||||
import './unit/Canvas-test.ts';
|
import './unit/Canvas-test.ts';
|
||||||
import './unit/Container-test.ts';
|
import './unit/Container-test.ts';
|
||||||
import './unit/Context-test.ts';
|
import './unit/Context-test.ts';
|
||||||
|
@ -1550,6 +1550,53 @@ describe('Shape', function () {
|
|||||||
compareCanvases(canvas2, canvas1, 240, 110);
|
compareCanvases(canvas2, canvas1, 240, 110);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('export stage when buffer canvas is for line', async function () {
|
||||||
|
var stage = addStage();
|
||||||
|
|
||||||
|
var layer = new Konva.Layer();
|
||||||
|
stage.add(layer);
|
||||||
|
|
||||||
|
const group = new Konva.Group({
|
||||||
|
id: 'group01',
|
||||||
|
draggable: false,
|
||||||
|
opacity: 0.99,
|
||||||
|
});
|
||||||
|
layer.add(group);
|
||||||
|
|
||||||
|
const arrow = new Konva.Arrow({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
points: [50, 25, 200, 25, 200, 225, 400, 225],
|
||||||
|
stroke: 'purple',
|
||||||
|
fill: 'purple',
|
||||||
|
strokeWidth: 4,
|
||||||
|
pointerAtEnding: true,
|
||||||
|
bezier: true,
|
||||||
|
});
|
||||||
|
group.add(arrow);
|
||||||
|
|
||||||
|
const bounds = layer.getClientRect({ relativeTo: stage });
|
||||||
|
const pos = stage.getPosition();
|
||||||
|
|
||||||
|
const canvas1 = layer.toCanvas({
|
||||||
|
pixelRatio: 1,
|
||||||
|
x: bounds.x + pos.x,
|
||||||
|
y: bounds.y + pos.y,
|
||||||
|
width: bounds.width,
|
||||||
|
height: bounds.height,
|
||||||
|
});
|
||||||
|
group.opacity(1);
|
||||||
|
const canvas2 = layer.toCanvas({
|
||||||
|
pixelRatio: 1,
|
||||||
|
x: bounds.x + pos.x,
|
||||||
|
y: bounds.y + pos.y,
|
||||||
|
width: bounds.width,
|
||||||
|
height: bounds.height,
|
||||||
|
});
|
||||||
|
|
||||||
|
compareCanvases(canvas1, canvas2, 240, 110);
|
||||||
|
});
|
||||||
|
|
||||||
// ======================================================
|
// ======================================================
|
||||||
it('optional disable shadow for stroke', function () {
|
it('optional disable shadow for stroke', function () {
|
||||||
var stage = addStage();
|
var stage = addStage();
|
||||||
|
@ -98,8 +98,8 @@ describe('Tween', function () {
|
|||||||
|
|
||||||
tween.destroy();
|
tween.destroy();
|
||||||
|
|
||||||
assert.equal(Konva.Tween.tweens[circle._id].x, undefined);
|
assert.equal(Konva.Tween.tweens[circle._id], undefined);
|
||||||
assert.equal(Konva.Tween.attrs[circle._id][tween._id], undefined);
|
assert.equal(Konva.Tween.attrs[circle._id], undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ======================================================
|
// ======================================================
|
||||||
@ -238,7 +238,7 @@ describe('Tween', function () {
|
|||||||
duration: 0.1,
|
duration: 0.1,
|
||||||
onFinish: function () {
|
onFinish: function () {
|
||||||
assert.equal(circle.x(), stage.width() / 2);
|
assert.equal(circle.x(), stage.width() / 2);
|
||||||
assert.equal(Object.keys(Konva.Tween.attrs[circle._id]).length, 0);
|
assert.equal(Konva.Tween.attrs[circle._id], undefined);
|
||||||
done();
|
done();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -305,16 +305,10 @@ describe('Tween', function () {
|
|||||||
points: [100, 100, 200, 100, 200, 200, 100, 200],
|
points: [100, 100, 200, 100, 200, 200, 100, 200],
|
||||||
duration: 0.1,
|
duration: 0.1,
|
||||||
onFinish: function () {
|
onFinish: function () {
|
||||||
assert.deepEqual(line.points(), [
|
assert.deepEqual(
|
||||||
100,
|
line.points(),
|
||||||
100,
|
[100, 100, 200, 100, 200, 200, 100, 200]
|
||||||
200,
|
);
|
||||||
100,
|
|
||||||
200,
|
|
||||||
200,
|
|
||||||
100,
|
|
||||||
200,
|
|
||||||
]);
|
|
||||||
done();
|
done();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -364,16 +358,10 @@ describe('Tween', function () {
|
|||||||
tween.reverse();
|
tween.reverse();
|
||||||
},
|
},
|
||||||
onReset: function () {
|
onReset: function () {
|
||||||
assert.deepEqual(line.points(), [
|
assert.deepEqual(
|
||||||
100,
|
line.points(),
|
||||||
100,
|
[100, 100, 200, 100, 200, 200, 100, 200]
|
||||||
200,
|
);
|
||||||
100,
|
|
||||||
200,
|
|
||||||
200,
|
|
||||||
100,
|
|
||||||
200,
|
|
||||||
]);
|
|
||||||
done();
|
done();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user