diff --git a/src/Context.ts b/src/Context.ts index aeac2e07..751901ab 100644 --- a/src/Context.ts +++ b/src/Context.ts @@ -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; diff --git a/src/Shape.ts b/src/Shape.ts index f0ffdffa..cd9d5d88 100644 --- a/src/Shape.ts +++ b/src/Shape.ts @@ -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; @@ -214,14 +215,14 @@ export class Shape< } /** - * @deprecated + * @deprecated */ getContext() { Util.warn('shape.getContext() method is deprecated. Please do not use it.'); return this.getLayer()!.getContext(); } /** - * @deprecated + * @deprecated */ getCanvas() { Util.warn('shape.getCanvas() method is deprecated. Please do not use it.'); @@ -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, this>; lineCap: GetSet; lineJoin: GetSet; + miterLimit: GetSet; perfectDrawEnabled: GetSet; sceneFunc: GetSet, this>; shadowColor: GetSet; @@ -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'); /** diff --git a/test/unit/Shape-test.ts b/test/unit/Shape-test.ts index 7c933e76..8c3070dd 100644 --- a/test/unit/Shape-test.ts +++ b/test/unit/Shape-test.ts @@ -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); + }); });