diff --git a/package.json b/package.json index add72ebb..55e356c3 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,8 @@ "test": "npm run test:browser && npm run test:node", "test:build": "parcel build ./test/unit-tests.html --dist-dir ./test-build --target none --public-url ./ --no-source-maps", "test:browser": "npm run test:build && mocha-headless-chrome -f ./test-build/unit-tests.html -a disable-web-security", - "test:node": "ts-mocha -p ./test/tsconfig.json test/unit/**/*.ts --exit && npm run test:import", - "test:watch": "rm -rf ./parcel-cache && parcel serve ./test/unit-tests.html ./test/manual-tests.html ./test/sandbox.html ./test/text-paths.html", + "test:watch": "rm -rf ./.parcel-cache && parcel serve ./test/unit-tests.html ./test/manual-tests.html ./test/sandbox.html ./test/text-paths.html ./test/bunnies.html", + "test:node": "ts-mocha -r ./test/node-global-setup.mjs -p ./test/tsconfig.json test/unit/**/*.ts --exit && npm run test:import", "tsc": "tsc --removeComments", "rollup": "rollup -c --bundleConfigAsCjs", "clean": "rm -rf ./lib && rm -rf ./types && rm -rf ./cmj && rm -rf ./test-build", diff --git a/src/Container.ts b/src/Container.ts index a6abfed6..aefd685a 100644 --- a/src/Container.ts +++ b/src/Container.ts @@ -7,9 +7,10 @@ import { Shape } from './Shape'; import { HitCanvas, SceneCanvas } from './Canvas'; import { SceneContext } from './Context'; +export type ClipFuncOutput = void | [Path2D | CanvasFillRule] | [Path2D, CanvasFillRule] export interface ContainerConfig extends NodeConfig { clearBeforeDraw?: boolean; - clipFunc?: (ctx: SceneContext) => void; + clipFunc?: (ctx: SceneContext) => ClipFuncOutput; clipX?: number; clipY?: number; clipWidth?: number; @@ -396,14 +397,15 @@ export abstract class Container< var m = transform.getMatrix(); context.transform(m[0], m[1], m[2], m[3], m[4], m[5]); context.beginPath(); + let clipArgs; if (clipFunc) { - clipFunc.call(this, context, this); + clipArgs = clipFunc.call(this, context, this); } else { var clipX = this.clipX(); var clipY = this.clipY(); context.rect(clipX, clipY, clipWidth, clipHeight); } - context.clip(); + context.clip.apply(context, clipArgs); m = transform.copy().invert().getMatrix(); context.transform(m[0], m[1], m[2], m[3], m[4], m[5]); } @@ -519,7 +521,7 @@ export abstract class Container< // there was "this" instead of "Container", // but it breaks react-konva types: https://github.com/konvajs/react-konva/issues/390 clipFunc: GetSet< - (ctx: CanvasRenderingContext2D, shape: Container) => void, + (ctx: CanvasRenderingContext2D, shape: Container) => ClipFuncOutput, this >; } @@ -638,5 +640,8 @@ Factory.addGetterSetter(Container, 'clipFunc'); * // set clip height * container.clipFunc(function(ctx) { * ctx.rect(0, 0, 100, 100); + * + * // optionally return a clip Path2D and clip-rule or just the clip-rule + * return [new Path2D('M0 0v50h50Z'), 'evenodd'] * }); */ diff --git a/src/Context.ts b/src/Context.ts index 54b4687e..1d7c53a8 100644 --- a/src/Context.ts +++ b/src/Context.ts @@ -362,8 +362,10 @@ export class Context { * @method * @name Konva.Context#clip */ - clip() { - this._context.clip(); + clip(fillRule?: CanvasFillRule): void; + clip(path: Path2D, fillRule?: CanvasFillRule): void; + clip(...args: any[]) { + this._context.clip.apply(this._context, args); } /** * closePath function. @@ -482,12 +484,11 @@ export class Context { * @method * @name Konva.Context#fill */ - fill(path2d?: Path2D) { - if (path2d) { - this._context.fill(path2d); - } else { - this._context.fill(); - } + fill(fillRule?: CanvasFillRule): void; + fill(path: Path2D, fillRule?: CanvasFillRule): void; + fill(...args: any[]) { + // this._context.fill(); + this._context.fill.apply(this._context, args); } /** * fillRect function. @@ -749,7 +750,7 @@ export class Context { // supported context properties type CanvasContextProps = Pick< CanvasRenderingContext2D, - typeof CONTEXT_PROPERTIES[number] + (typeof CONTEXT_PROPERTIES)[number] >; export interface Context extends CanvasContextProps {} diff --git a/src/Shape.ts b/src/Shape.ts index 2c2cd393..93fd096e 100644 --- a/src/Shape.ts +++ b/src/Shape.ts @@ -56,6 +56,7 @@ export interface ShapeConfig extends NodeConfig { fillRadialGradientColorStops?: Array; fillEnabled?: boolean; fillPriority?: string; + fillRule?: CanvasFillRule; stroke?: string | CanvasGradient; strokeWidth?: number; fillAfterStrokeEnabled?: boolean; @@ -88,6 +89,11 @@ export interface ShapeGetClientRectConfig { relativeTo?: Node; } +export type FillFuncOutput = + | void + | [Path2D | CanvasFillRule] + | [Path2D, CanvasFillRule]; + var HAS_SHADOW = 'hasShadow'; var SHADOW_RGBA = 'shadowRGBA'; var patternImage = 'patternImage'; @@ -112,7 +118,12 @@ export const shapes: { [key: string]: Shape } = {}; // what color to use for hit test? function _fillFunc(context) { - context.fill(); + const fillRule = this.attrs.fillRule; + if (fillRule) { + context.fill(fillRule); + } else { + context.fill(); + } } function _strokeFunc(context) { context.stroke(); @@ -176,7 +187,7 @@ export class Shape< _centroid: boolean; colorKey: string; - _fillFunc: (ctx: Context) => void; + _fillFunc: (ctx: Context) => FillFuncOutput; _strokeFunc: (ctx: Context) => void; _fillFuncHit: (ctx: Context) => void; _strokeFuncHit: (ctx: Context) => void; @@ -1987,6 +1998,22 @@ Factory.addGetterSetter(Shape, 'fillPatternRotation', 0); * shape.fillPatternRotation(20); */ +Factory.addGetterSetter(Shape, 'fillRule', undefined, getStringValidator()); + +/** + * get/set fill rule + * @name Konva.Shape#fillRule + * @method + * @param {CanvasFillRule} rotation + * @returns {Konva.Shape} + * @example + * // get fill rule + * var fillRule = shape.fillRule(); + * + * // set fill rule + * shape.fillRule('evenodd); + */ + Factory.backCompat(Shape, { dashArray: 'dash', getDashArray: 'getDash', diff --git a/test/performance/bunnies.html b/test/bunnies.html similarity index 95% rename from test/performance/bunnies.html rename to test/bunnies.html index bdb244bf..f40aba22 100644 --- a/test/performance/bunnies.html +++ b/test/bunnies.html @@ -10,12 +10,9 @@
- - - - -