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); this.setAttr('lineJoin', lineJoin);
} }
} }
_applyMiterLimit(shape: Shape) {
const miterLimit = shape.attrs.miterLimit;
if (miterLimit != null) {
this.setAttr('miterLimit', miterLimit);
}
}
setAttr(attr: string, val) { setAttr(attr: string, val) {
this._context[attr] = val; this._context[attr] = val;

View File

@@ -66,6 +66,7 @@ export interface ShapeConfig extends NodeConfig {
strokeEnabled?: boolean; strokeEnabled?: boolean;
lineJoin?: LineJoin; lineJoin?: LineJoin;
lineCap?: LineCap; lineCap?: LineCap;
miterLimit?: number;
sceneFunc?: (con: Context, shape: Shape) => void; sceneFunc?: (con: Context, shape: Shape) => void;
hitFunc?: (con: Context, shape: Shape) => void; hitFunc?: (con: Context, shape: Shape) => void;
shadowColor?: string; shadowColor?: string;
@@ -637,6 +638,7 @@ export class Shape<
bufferContext.clear(); bufferContext.clear();
bufferContext.save(); bufferContext.save();
bufferContext._applyLineJoin(this); bufferContext._applyLineJoin(this);
bufferContext._applyMiterLimit(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
const 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]);
@@ -660,6 +662,7 @@ export class Shape<
); );
} else { } else {
context._applyLineJoin(this); context._applyLineJoin(this);
context._applyMiterLimit(this);
if (!cachingSelf) { if (!cachingSelf) {
const o = this.getAbsoluteTransform(top).getMatrix(); const o = this.getAbsoluteTransform(top).getMatrix();
@@ -710,6 +713,7 @@ export class Shape<
} }
context.save(); context.save();
context._applyLineJoin(this); context._applyLineJoin(this);
context._applyMiterLimit(this);
const selfCache = this === top; const selfCache = this === top;
if (!selfCache) { if (!selfCache) {
@@ -828,6 +832,7 @@ export class Shape<
hitFunc: GetSet<ShapeConfigHandler<this>, this>; hitFunc: GetSet<ShapeConfigHandler<this>, this>;
lineCap: GetSet<LineCap, this>; lineCap: GetSet<LineCap, this>;
lineJoin: GetSet<LineJoin, this>; lineJoin: GetSet<LineJoin, this>;
miterLimit: GetSet<number, this>;
perfectDrawEnabled: GetSet<boolean, this>; perfectDrawEnabled: GetSet<boolean, this>;
sceneFunc: GetSet<ShapeConfigHandler<this>, this>; sceneFunc: GetSet<ShapeConfigHandler<this>, this>;
shadowColor: GetSet<string, this>; shadowColor: GetSet<string, this>;
@@ -1084,6 +1089,22 @@ Factory.addGetterSetter(Shape, 'lineCap');
* shape.lineCap('round'); * 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'); Factory.addGetterSetter(Shape, 'sceneFunc');
/** /**

View File

@@ -971,6 +971,9 @@ describe('Shape', function () {
rect.strokeWidth(8); rect.strokeWidth(8);
assert.equal(rect.strokeWidth(), 8); assert.equal(rect.strokeWidth(), 8);
rect.miterLimit(5);
assert.equal(rect.miterLimit(), 5);
const f = () => {}; const f = () => {};
rect.sceneFunc(f); rect.sceneFunc(f);
assert.equal(rect.sceneFunc(), f); assert.equal(rect.sceneFunc(), f);
@@ -2408,4 +2411,59 @@ describe('Shape', function () {
const hitShape = layer.getIntersection({ x: 150, y: 150 }); const hitShape = layer.getIntersection({ x: 150, y: 150 });
assert.equal(hitShape, null); 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);
});
}); });