feat: add support for miter-limit

This commit is contained in:
Nathan Muir
2025-08-19 11:45:17 +12:00
committed by Nathan Muir
parent c621b39f99
commit fc227431cc
3 changed files with 87 additions and 2 deletions

View File

@@ -304,6 +304,12 @@ export class Context {
this.setAttr('lineJoin', lineJoin);
}
}
_applyMiterLimit(shape: Shape) {
const miterLimit = shape.attrs.miterLimit;
if (miterLimit != null) {
this.setAttr('miterLimit', miterLimit);
}
}
setAttr(attr: string, val) {
this._context[attr] = val;

View File

@@ -66,6 +66,7 @@ export interface ShapeConfig extends NodeConfig {
strokeEnabled?: boolean;
lineJoin?: LineJoin;
lineCap?: LineCap;
miterLimit?: number;
sceneFunc?: (con: Context, shape: Shape) => void;
hitFunc?: (con: Context, shape: Shape) => void;
shadowColor?: string;
@@ -637,6 +638,7 @@ export class Shape<
bufferContext.clear();
bufferContext.save();
bufferContext._applyLineJoin(this);
bufferContext._applyMiterLimit(this);
// layer might be undefined if we are using cache before adding to layer
const o = this.getAbsoluteTransform(top).getMatrix();
bufferContext.transform(o[0], o[1], o[2], o[3], o[4], o[5]);
@@ -660,6 +662,7 @@ export class Shape<
);
} else {
context._applyLineJoin(this);
context._applyMiterLimit(this);
if (!cachingSelf) {
const o = this.getAbsoluteTransform(top).getMatrix();
@@ -710,6 +713,7 @@ export class Shape<
}
context.save();
context._applyLineJoin(this);
context._applyMiterLimit(this);
const selfCache = this === top;
if (!selfCache) {
@@ -828,6 +832,7 @@ export class Shape<
hitFunc: GetSet<ShapeConfigHandler<this>, this>;
lineCap: GetSet<LineCap, this>;
lineJoin: GetSet<LineJoin, this>;
miterLimit: GetSet<number, this>;
perfectDrawEnabled: GetSet<boolean, this>;
sceneFunc: GetSet<ShapeConfigHandler<this>, this>;
shadowColor: GetSet<string, this>;
@@ -1084,6 +1089,22 @@ Factory.addGetterSetter(Shape, 'lineCap');
* shape.lineCap('round');
*/
Factory.addGetterSetter(Shape, 'miterLimit');
/**
* get/set miterLimit.
* @name Konva.Shape#miterLimit
* @method
* @param {Number} miterLimit
* @returns {Number}
* @example
* // get miter limit
* var miterLimit = shape.miterLimit();
*
* // set miter limit
* shape.miterLimit(10);
*/
Factory.addGetterSetter(Shape, 'sceneFunc');
/**

View File

@@ -971,6 +971,9 @@ describe('Shape', function () {
rect.strokeWidth(8);
assert.equal(rect.strokeWidth(), 8);
rect.miterLimit(5);
assert.equal(rect.miterLimit(), 5);
const f = () => {};
rect.sceneFunc(f);
assert.equal(rect.sceneFunc(), f);
@@ -2408,4 +2411,59 @@ describe('Shape', function () {
const hitShape = layer.getIntersection({ x: 150, y: 150 });
assert.equal(hitShape, null);
});
it('miterLimit with buffer canvas', function () {
var stage = addStage();
var layer = new Konva.Layer();
// Sharp triangle with opacity to force buffer canvas usage
var triangle = new Konva.Shape({
x: 100,
y: 50,
sceneFunc: function (ctx, shape) {
ctx.beginPath();
ctx.moveTo(140, 5);
ctx.lineTo(0, 70);
ctx.lineTo(220, 70);
ctx.closePath();
ctx.fillStrokeShape(shape);
},
fill: 'yellow',
stroke: 'orange',
strokeWidth: 10,
lineJoin: 'miter',
miterLimit: 3,
opacity: 0.7, // Forces buffer canvas usage
});
layer.add(triangle);
stage.add(layer);
// Create expected result using native canvas with buffer approach
var canvas = createCanvas();
var context = canvas.getContext('2d');
// Draw on buffer first
context.beginPath();
context.translate(100, 50);
context.moveTo(140, 5);
context.lineTo(0, 70);
context.lineTo(220, 70);
context.closePath();
context.fillStyle = 'yellow';
context.strokeStyle = 'orange';
context.lineWidth = 10;
context.lineJoin = 'miter';
context.miterLimit = 3;
context.fill();
context.stroke();
// Apply opacity by drawing to final canvas
var finalCanvas = createCanvas();
var finalContext = finalCanvas.getContext('2d');
finalContext.globalAlpha = 0.7;
finalContext.drawImage(canvas, 0, 0);
compareLayerAndCanvas(layer, finalCanvas, 200);
});
});