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.
|
||||
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)
|
||||
|
||||
- Fix text rendering when ellipses are used
|
||||
|
@ -120,8 +120,6 @@ export abstract class Container<
|
||||
* layer.add(shape1, shape2, shape3);
|
||||
* // empty arrays are accepted, though each individual child must be defined
|
||||
* layer.add(...shapes);
|
||||
* // remember to redraw layer if you changed something
|
||||
* layer.draw();
|
||||
*/
|
||||
add(...children: ChildType[]) {
|
||||
if (children.length === 0) {
|
||||
|
@ -385,7 +385,7 @@ export class Layer extends Container<Group | Shape> {
|
||||
// empty pixel
|
||||
return {};
|
||||
}
|
||||
drawScene(can?: SceneCanvas, top?: Node) {
|
||||
drawScene(can?: SceneCanvas, top?: Node, bufferCanvas?: SceneCanvas) {
|
||||
const layer = this.getLayer(),
|
||||
canvas = can || (layer && layer.getCanvas());
|
||||
|
||||
@ -397,7 +397,7 @@ export class Layer extends Container<Group | Shape> {
|
||||
canvas.getContext().clear();
|
||||
}
|
||||
|
||||
Container.prototype.drawScene.call(this, canvas, top);
|
||||
Container.prototype.drawScene.call(this, canvas, top, bufferCanvas);
|
||||
|
||||
this._fire(DRAW, {
|
||||
node: this,
|
||||
|
33
src/Node.ts
33
src/Node.ts
@ -693,39 +693,32 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
||||
evtStr: 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) {
|
||||
return this._delegate.apply(this, arguments as any);
|
||||
}
|
||||
let events = (evtStr as string).split(SPACE),
|
||||
len = events.length,
|
||||
n,
|
||||
event,
|
||||
parts,
|
||||
baseEvent,
|
||||
name;
|
||||
const events = (evtStr as string).split(SPACE);
|
||||
|
||||
/*
|
||||
* loop through types and attach event listeners to
|
||||
* each one. eg. 'click mouseover.namespace mouseout'
|
||||
* will create three event bindings
|
||||
*/
|
||||
for (n = 0; n < len; n++) {
|
||||
event = events[n];
|
||||
parts = event.split('.');
|
||||
baseEvent = parts[0];
|
||||
name = parts[1] || '';
|
||||
for (let n = 0; n < events.length; n++) {
|
||||
const event = events[n];
|
||||
const parts = event.split('.');
|
||||
const baseEvent = parts[0];
|
||||
const name = parts[1] || '';
|
||||
|
||||
// create events array if it doesn't exist
|
||||
if (!this.eventListeners[baseEvent]) {
|
||||
this.eventListeners[baseEvent] = [];
|
||||
}
|
||||
|
||||
this.eventListeners[baseEvent].push({
|
||||
name: name,
|
||||
handler: handler,
|
||||
});
|
||||
this.eventListeners[baseEvent].push({ name , handler });
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -2208,7 +2201,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
||||
* node.addName('selected');
|
||||
* node.name(); // return 'red selected'
|
||||
*/
|
||||
addName(name) {
|
||||
addName(name: string) {
|
||||
if (!this.hasName(name)) {
|
||||
const oldName = this.name();
|
||||
const newName = oldName ? oldName + ' ' + name : name;
|
||||
@ -2380,7 +2373,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
||||
|
||||
const topListeners = this._getProtoListeners(eventType);
|
||||
if (topListeners) {
|
||||
for (var i = 0; i < topListeners.length; i++) {
|
||||
for (let i = 0; i < topListeners.length; i++) {
|
||||
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
|
||||
const selfListeners = this.eventListeners[eventType];
|
||||
if (selfListeners) {
|
||||
for (var i = 0; i < selfListeners.length; i++) {
|
||||
for (let i = 0; i < selfListeners.length; i++) {
|
||||
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
|
||||
|
||||
const layer = this.getLayer();
|
||||
let canvas = can || layer!.getCanvas(),
|
||||
const canvas = can || layer!.getCanvas(),
|
||||
context = canvas.getContext() as SceneContext,
|
||||
cachedCanvas = this._getCanvasCache(),
|
||||
drawFunc = this.getSceneFunc(),
|
||||
hasShadow = this.hasShadow(),
|
||||
stage,
|
||||
bufferContext;
|
||||
hasShadow = this.hasShadow();
|
||||
let stage, bufferContext;
|
||||
|
||||
const skipBuffer = canvas.isCache;
|
||||
const cachingSelf = top === this;
|
||||
@ -633,7 +632,7 @@ export class Shape<
|
||||
bufferContext.save();
|
||||
bufferContext._applyLineJoin(this);
|
||||
// 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]);
|
||||
|
||||
drawFunc.call(this, bufferContext, this);
|
||||
@ -651,7 +650,7 @@ export class Shape<
|
||||
context._applyLineJoin(this);
|
||||
|
||||
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._applyOpacity(this);
|
||||
context._applyGlobalCompositeOperation(this);
|
||||
|
@ -214,11 +214,11 @@ export class Stage extends Container<Layer> {
|
||||
*/
|
||||
setContainer(container) {
|
||||
if (typeof container === STRING) {
|
||||
let id;
|
||||
if (container.charAt(0) === '.') {
|
||||
const className = container.slice(1);
|
||||
container = document.getElementsByClassName(className)[0];
|
||||
} else {
|
||||
var id;
|
||||
if (container.charAt(0) !== '#') {
|
||||
id = container;
|
||||
} else {
|
||||
|
33
src/Tween.ts
33
src/Tween.ts
@ -200,8 +200,7 @@ export class Tween {
|
||||
nodeId = node._id,
|
||||
easing = config.easing || Easings.Linear,
|
||||
yoyo = !!config.yoyo;
|
||||
let duration,
|
||||
key;
|
||||
let duration, key;
|
||||
|
||||
if (typeof config.duration === 'undefined') {
|
||||
duration = 0.3;
|
||||
@ -268,11 +267,7 @@ export class Tween {
|
||||
_addAttr(key, end) {
|
||||
const node = this.node,
|
||||
nodeId = node._id;
|
||||
let diff,
|
||||
len,
|
||||
trueEnd,
|
||||
trueStart,
|
||||
endRGBA;
|
||||
let diff, len, trueEnd, trueStart, endRGBA;
|
||||
|
||||
// remove conflict from tween map if it exists
|
||||
const tweenId = Tween.tweens[nodeId][key];
|
||||
@ -352,14 +347,7 @@ export class Tween {
|
||||
_tweenFunc(i) {
|
||||
const node = this.node,
|
||||
attrs = Tween.attrs[node._id][this._id];
|
||||
let key,
|
||||
attr,
|
||||
start,
|
||||
diff,
|
||||
newVal,
|
||||
n,
|
||||
len,
|
||||
end;
|
||||
let key, attr, start, diff, newVal, n, len, end;
|
||||
|
||||
for (key in attrs) {
|
||||
attr = attrs[key];
|
||||
@ -528,11 +516,26 @@ export class Tween {
|
||||
|
||||
this.pause();
|
||||
|
||||
// Clean up animation
|
||||
if (this.anim) {
|
||||
this.anim.stop();
|
||||
}
|
||||
|
||||
// Clean up tween entries
|
||||
for (const key in attrs) {
|
||||
delete Tween.tweens[nodeId][key];
|
||||
}
|
||||
|
||||
// Clean up attrs entry
|
||||
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){
|
||||
* // image is Konva.Image instance
|
||||
* layer.add(image);
|
||||
* layer.draw();
|
||||
* });
|
||||
*/
|
||||
static fromURL(
|
||||
|
@ -102,26 +102,24 @@ export class Line<
|
||||
}
|
||||
|
||||
_sceneFunc(context: Context) {
|
||||
let points = this.points(),
|
||||
const points = this.points(),
|
||||
length = points.length,
|
||||
tension = this.tension(),
|
||||
closed = this.closed(),
|
||||
bezier = this.bezier(),
|
||||
tp,
|
||||
len,
|
||||
n;
|
||||
bezier = this.bezier();
|
||||
|
||||
if (!length) {
|
||||
return;
|
||||
}
|
||||
let n = 0;
|
||||
|
||||
context.beginPath();
|
||||
context.moveTo(points[0], points[1]);
|
||||
|
||||
// tension
|
||||
if (tension !== 0 && length > 4) {
|
||||
tp = this.getTensionPoints();
|
||||
len = tp.length;
|
||||
const tp = this.getTensionPoints();
|
||||
const len = tp.length;
|
||||
n = closed ? 0 : 4;
|
||||
|
||||
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.textDecoration] can be line-through, underline or empty string. Default is empty string.
|
||||
* @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 {Number} [config.padding]
|
||||
* @param {Number} [config.lineHeight] default is 1
|
||||
@ -885,6 +885,8 @@ Factory.addGetterSetter(Text, 'padding', 0, getNumberValidator());
|
||||
*
|
||||
* // align text to right
|
||||
* text.align('right');
|
||||
*
|
||||
* // justify text
|
||||
*/
|
||||
|
||||
Factory.addGetterSetter(Text, 'align', LEFT);
|
||||
|
@ -11,6 +11,7 @@
|
||||
<script type="module">
|
||||
// CORE
|
||||
import './unit/Animation-test.ts';
|
||||
import './unit/Tween-test.ts';
|
||||
import './unit/Canvas-test.ts';
|
||||
import './unit/Container-test.ts';
|
||||
import './unit/Context-test.ts';
|
||||
|
@ -1550,6 +1550,53 @@ describe('Shape', function () {
|
||||
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 () {
|
||||
var stage = addStage();
|
||||
|
@ -98,8 +98,8 @@ describe('Tween', function () {
|
||||
|
||||
tween.destroy();
|
||||
|
||||
assert.equal(Konva.Tween.tweens[circle._id].x, undefined);
|
||||
assert.equal(Konva.Tween.attrs[circle._id][tween._id], undefined);
|
||||
assert.equal(Konva.Tween.tweens[circle._id], undefined);
|
||||
assert.equal(Konva.Tween.attrs[circle._id], undefined);
|
||||
});
|
||||
|
||||
// ======================================================
|
||||
@ -238,7 +238,7 @@ describe('Tween', function () {
|
||||
duration: 0.1,
|
||||
onFinish: function () {
|
||||
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();
|
||||
},
|
||||
});
|
||||
@ -305,16 +305,10 @@ describe('Tween', function () {
|
||||
points: [100, 100, 200, 100, 200, 200, 100, 200],
|
||||
duration: 0.1,
|
||||
onFinish: function () {
|
||||
assert.deepEqual(line.points(), [
|
||||
100,
|
||||
100,
|
||||
200,
|
||||
100,
|
||||
200,
|
||||
200,
|
||||
100,
|
||||
200,
|
||||
]);
|
||||
assert.deepEqual(
|
||||
line.points(),
|
||||
[100, 100, 200, 100, 200, 200, 100, 200]
|
||||
);
|
||||
done();
|
||||
},
|
||||
});
|
||||
@ -364,16 +358,10 @@ describe('Tween', function () {
|
||||
tween.reverse();
|
||||
},
|
||||
onReset: function () {
|
||||
assert.deepEqual(line.points(), [
|
||||
100,
|
||||
100,
|
||||
200,
|
||||
100,
|
||||
200,
|
||||
200,
|
||||
100,
|
||||
200,
|
||||
]);
|
||||
assert.deepEqual(
|
||||
line.points(),
|
||||
[100, 100, 200, 100, 200, 200, 100, 200]
|
||||
);
|
||||
done();
|
||||
},
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user