first commit
This commit is contained in:
+22
@@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Original work Copyright (C) 2011 - 2013 by Eric Rowell (KineticJS)
|
||||
Modified work Copyright (C) 2014 - present by Anton Lavrenov (Konva)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
<p align="center">
|
||||
<img src="https://konvajs.org/img/icon.png" alt="Konva logo" height="60" />
|
||||
</p>
|
||||
|
||||
<h1 align="center">Konva</h1>
|
||||
|
||||
<p align="center"><strong>Build interactive graphics, editors, and diagrams for the web.</strong></p>
|
||||
|
||||
[](https://www.npmjs.com/package/konva)
|
||||
[](https://badge.fury.io/js/konva)
|
||||
[](https://opencollective.com/konva)
|
||||
[](https://github.com/konvajs/konva/actions/workflows/test-browser.yml)
|
||||
[](https://github.com/konvajs/konva/actions/workflows/test-node.yml)
|
||||
[](https://cdnjs.com/libraries/konva)
|
||||
|
||||
Konva is an open-source 2D canvas framework for interactive graphics. Its scene graph gives each shape its own events, drag behavior, transforms, animation, cache, and export controls.
|
||||
|
||||
Use Konva for design editors, whiteboards, diagrams, annotations, maps, and other visual tools. Konva is MIT licensed and does not require a license key.
|
||||
|
||||
This repository began as a GitHub fork of [ericdrowell/KineticJS](https://github.com/ericdrowell/KineticJS).
|
||||
|
||||
- **Visit:** The [Home Page](https://konvajs.org/) and follow on [Twitter](https://twitter.com/lavrton)
|
||||
- **Discover:** [Tutorials](https://konvajs.org/docs/index.html), [API Documentation](https://konvajs.org/api/Konva.html)
|
||||
- **Try it:** [Canvas Editor](https://konvajs.org/docs/sandbox/Canvas_Editor.html), [Free Drawing](https://konvajs.org/docs/sandbox/Free_Drawing.html), [Image Crop](https://konvajs.org/docs/sandbox/Canvas_Crop_Image.html), [Window Frame Designer](https://konvajs.org/docs/sandbox/Window_Frame_Designer.html), and [more demos](https://konvajs.org/docs/sandbox.html)
|
||||
- **Used by:** [Polotno](https://polotno.com/?utm_source=konvajs) (design editor SDK) and many others — see the full showcase on the [home page](https://konvajs.org/)
|
||||
- **Help:** [StackOverflow](https://stackoverflow.com/questions/tagged/konvajs), [Discord Chat](https://discord.gg/8FqZwVT)
|
||||
- **Support the project:** [Star Konva on GitHub](https://github.com/konvajs/konva)
|
||||
|
||||
[](https://konvajs.org/docs/select_and_transform/Basic_demo.html)
|
||||
|
||||
## Framework integrations
|
||||
|
||||
| Framework | Package | Documentation |
|
||||
| --------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| React | [`react-konva`](https://www.npmjs.com/package/react-konva) | [React guide](https://konvajs.org/docs/react/index.html) |
|
||||
| Vue | [`vue-konva`](https://www.npmjs.com/package/vue-konva) | [Vue guide](https://konvajs.org/docs/vue/index.html) |
|
||||
| Svelte | [`svelte-konva`](https://www.npmjs.com/package/svelte-konva) | [Svelte guide](https://konvajs.org/docs/svelte/index.html) |
|
||||
| Angular | [`ng2-konva`](https://www.npmjs.com/package/ng2-konva) | [Angular guide](https://konvajs.org/docs/angular/index.html) |
|
||||
|
||||
# Quick Look
|
||||
|
||||
```javascript
|
||||
import Konva from 'konva';
|
||||
|
||||
const container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
|
||||
const stage = new Konva.Stage({ container, width: 600, height: 400 });
|
||||
const layer = new Konva.Layer();
|
||||
const box = new Konva.Rect({
|
||||
x: 50,
|
||||
y: 50,
|
||||
width: 120,
|
||||
height: 80,
|
||||
fill: '#00a8e8',
|
||||
draggable: true,
|
||||
});
|
||||
|
||||
layer.add(box);
|
||||
stage.add(layer);
|
||||
```
|
||||
|
||||
> **Building a full design editor?** [Polotno](https://polotno.com/?utm_source=konvajs&utm_medium=readme&utm_content=konva) is a commercial design editor SDK built on Konva by the Konva maintainers. It ships templates, text editing, and export, so you integrate an editor instead of building one: `npm install polotno`.
|
||||
|
||||
# Browsers support
|
||||
|
||||
Konva works in modern mobile and desktop browsers that support ES2018.
|
||||
|
||||
# Debugging
|
||||
|
||||
The Chrome inspector simply shows the canvas element. To see the Konva objects and their details, install the konva-dev extension at https://github.com/konvajs/konva-devtool.
|
||||
|
||||
# Loading and installing Konva
|
||||
|
||||
Konva supports UMD loading. So you can use all possible variants to load the framework into your project:
|
||||
|
||||
### Load Konva via classical `<script>` tag from CDN:
|
||||
|
||||
```html
|
||||
<script src="https://unpkg.com/konva@10/konva.min.js"></script>
|
||||
```
|
||||
|
||||
### Install with npm:
|
||||
|
||||
```bash
|
||||
npm install konva
|
||||
```
|
||||
|
||||
```javascript
|
||||
// The modern way (e.g. an ES6-style import for webpack, parcel)
|
||||
import Konva from 'konva';
|
||||
```
|
||||
|
||||
#### Typescript usage
|
||||
|
||||
Add DOM definitions into your `tsconfig.json`:
|
||||
|
||||
```
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"es6",
|
||||
"dom"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3 Minimal bundle
|
||||
|
||||
```javascript
|
||||
import Konva from 'konva/lib/Core';
|
||||
// Now you have a Konva object with Stage, Layer, FastLayer, Group, Shape and some additional utils function.
|
||||
// Also core currently already have support for drag&drop and animations.
|
||||
// BUT there are no shapes (rect, circle, etc), no filters.
|
||||
|
||||
// but you can simply add anything you need:
|
||||
import { Rect } from 'konva/lib/shapes/Rect';
|
||||
// importing a shape will automatically inject it into Konva object
|
||||
|
||||
var rect1 = new Rect();
|
||||
// or:
|
||||
var shape = new Konva.Rect();
|
||||
|
||||
// for filters you can use this:
|
||||
import { Blur } from 'konva/lib/filters/Blur';
|
||||
```
|
||||
|
||||
### 4 NodeJS env
|
||||
|
||||
In order to run `konva` in nodejs environment you also need to install `canvas` or `skia-canvas` package manually for rendering backend.
|
||||
|
||||
```bash
|
||||
# node-canvas backend
|
||||
npm install konva canvas
|
||||
# skia-canvas backend
|
||||
npm install konva skia-canvas
|
||||
```
|
||||
|
||||
Use the same scene graph API for server-side rendering and omit the stage's `container` attribute. DOM events require a browser, and canvas feature support depends on the selected backend.
|
||||
|
||||
```js
|
||||
import Konva from 'konva';
|
||||
import 'konva/canvas-backend'; // or import 'konva/skia-backend';
|
||||
|
||||
const stage = new Konva.Stage({
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
// Add shapes and export the stage with the usual Konva drawing API.
|
||||
```
|
||||
|
||||
# Backers
|
||||
|
||||
Konva is maintained by the team behind [Polotno](https://polotno.com/?utm_source=konvajs&utm_medium=readme&utm_content=konva-backers), a commercial design editor SDK built on Konva, which sponsors ongoing development.
|
||||
|
||||

|
||||

|
||||
|
||||
- [myposter GmbH](https://www.myposter.de/)
|
||||
- [queue.gg](https://queue.gg/)
|
||||
|
||||
# Change log
|
||||
|
||||
See [CHANGELOG.md](https://github.com/konvajs/konva/blob/master/CHANGELOG.md).
|
||||
|
||||
## Building the Konva Framework
|
||||
|
||||
To make a full build run `npm run build`. The command will compile all typescript files, combine then into one bundle and run minifier.
|
||||
|
||||
## Testing
|
||||
|
||||
Konva uses Mocha for testing.
|
||||
|
||||
- If you need run test only one time run `npm run test`.
|
||||
- While developing it is easy to use `npm start`. Just run it and go to [http://localhost:1234/unit-tests.html](http://localhost:1234/unit-tests.html). The watcher will rebuild the bundle on any change.
|
||||
|
||||
Konva is covered with hundreds of tests and well over a thousand assertions.
|
||||
For each feature or bug fix, write a failing test first, implement the behavior, then verify that the test passes (test-driven development).
|
||||
|
||||
## Generate documentation
|
||||
|
||||
Run `npm run build`, then `npx gulp api` to generate documentation from the current bundle in the `api` folder.
|
||||
|
||||
# Pull Requests
|
||||
|
||||
I'd be happy to review any pull requests that may better the Konva project,
|
||||
in particular if you have a bug fix, enhancement, or a new shape (see `src/shapes` for examples). Before doing so, please first make sure that all of the tests pass (`npm run test`).
|
||||
|
||||
## Contributors
|
||||
|
||||
### Financial Contributors
|
||||
|
||||
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/konva/contribute)]
|
||||
|
||||
#### Individuals
|
||||
|
||||
<a href="https://opencollective.com/konva"><img src="https://opencollective.com/konva/individuals.svg?width=890"></a>
|
||||
|
||||
#### Organizations
|
||||
|
||||
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/konva/contribute)]
|
||||
|
||||
<a href="https://opencollective.com/konva/organization/0/website"><img src="https://opencollective.com/konva/organization/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/konva/organization/1/website"><img src="https://opencollective.com/konva/organization/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/konva/organization/2/website"><img src="https://opencollective.com/konva/organization/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/konva/organization/3/website"><img src="https://opencollective.com/konva/organization/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/konva/organization/4/website"><img src="https://opencollective.com/konva/organization/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/konva/organization/5/website"><img src="https://opencollective.com/konva/organization/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/konva/organization/6/website"><img src="https://opencollective.com/konva/organization/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/konva/organization/7/website"><img src="https://opencollective.com/konva/organization/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/konva/organization/8/website"><img src="https://opencollective.com/konva/organization/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/konva/organization/9/website"><img src="https://opencollective.com/konva/organization/9/avatar.svg"></a>
|
||||
+20198
File diff suppressed because it is too large
Load Diff
+54
File diff suppressed because one or more lines are too long
+84
@@ -0,0 +1,84 @@
|
||||
import type { Layer } from './Layer.ts';
|
||||
import type { IFrame, AnimationFn } from './types.ts';
|
||||
/**
|
||||
* Animation constructor.
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @param {AnimationFn} func function executed on each animation frame. The function is passed a frame object, which contains
|
||||
* timeDiff, lastTime, time, and frameRate properties. The timeDiff property is the number of milliseconds that have passed
|
||||
* since the last animation frame. The time property accumulates elapsed animation time in milliseconds across stop/start calls, excluding paused time.
|
||||
* The lastTime property is the current frame's clock timestamp in milliseconds. The frameRate property is the current frame rate in frames / second.
|
||||
* Return false from function, if you don't need to redraw layer/layers on some frames.
|
||||
* @param {Konva.Layer|Array} [layers] layer(s) to be redrawn on each animation frame. Can be a layer, an array of layers, or null.
|
||||
* Not specifying a node will result in no redraw.
|
||||
* @example
|
||||
* // move a node to the right at 50 pixels / second
|
||||
* var velocity = 50;
|
||||
*
|
||||
* var anim = new Konva.Animation(function(frame) {
|
||||
* var dist = velocity * (frame.timeDiff / 1000);
|
||||
* node.move({x: dist, y: 0});
|
||||
* }, layer);
|
||||
*
|
||||
* anim.start();
|
||||
*/
|
||||
export declare class Animation {
|
||||
func: AnimationFn;
|
||||
id: number;
|
||||
layers: Layer[];
|
||||
frame: IFrame;
|
||||
constructor(func: AnimationFn, layers?: any);
|
||||
/**
|
||||
* set layers to be redrawn on each animation frame
|
||||
* @method
|
||||
* @name Konva.Animation#setLayers
|
||||
* @param {Konva.Layer|Array} [layers] layer(s) to be redrawn. Can be a layer, an array of layers, or null. Not specifying a node will result in no redraw.
|
||||
* @return {Konva.Animation} this
|
||||
*/
|
||||
setLayers(layers: null | Layer | Layer[]): this;
|
||||
/**
|
||||
* get layers
|
||||
* @method
|
||||
* @name Konva.Animation#getLayers
|
||||
* @return {Array} Array of Konva.Layer
|
||||
*/
|
||||
getLayers(): Layer[];
|
||||
/**
|
||||
* add layer. Returns true if the layer was added, and false if it was not
|
||||
* @method
|
||||
* @name Konva.Animation#addLayer
|
||||
* @param {Konva.Layer} layer to add
|
||||
* @return {Bool} true if layer is added to animation, otherwise false
|
||||
*/
|
||||
addLayer(layer: Layer): boolean;
|
||||
/**
|
||||
* determine if animation is running or not. returns true or false
|
||||
* @method
|
||||
* @name Konva.Animation#isRunning
|
||||
* @return {Bool} is animation running?
|
||||
*/
|
||||
isRunning(): boolean;
|
||||
/**
|
||||
* start animation
|
||||
* @method
|
||||
* @name Konva.Animation#start
|
||||
* @return {Konva.Animation} this
|
||||
*/
|
||||
start(): this;
|
||||
/**
|
||||
* stop animation
|
||||
* @method
|
||||
* @name Konva.Animation#stop
|
||||
* @return {Konva.Animation} this
|
||||
*/
|
||||
stop(): this;
|
||||
_updateFrameObject(time: number): void;
|
||||
static animations: Set<Animation>;
|
||||
static animIdCounter: number;
|
||||
static animRunning: boolean;
|
||||
static _addAnimation(anim: Animation): void;
|
||||
static _removeAnimation(anim: Animation): void;
|
||||
static _runFrames(): void;
|
||||
static _animationLoop(): void;
|
||||
static _handleAnimation(): void;
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
var _a;
|
||||
import { glob } from "./Global.js";
|
||||
import { Node } from "./Node.js";
|
||||
import { Util } from "./Util.js";
|
||||
const now = ((_a = glob.performance) === null || _a === void 0 ? void 0 : _a.now)
|
||||
? () => glob.performance.now()
|
||||
: Date.now;
|
||||
/**
|
||||
* Animation constructor.
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @param {AnimationFn} func function executed on each animation frame. The function is passed a frame object, which contains
|
||||
* timeDiff, lastTime, time, and frameRate properties. The timeDiff property is the number of milliseconds that have passed
|
||||
* since the last animation frame. The time property accumulates elapsed animation time in milliseconds across stop/start calls, excluding paused time.
|
||||
* The lastTime property is the current frame's clock timestamp in milliseconds. The frameRate property is the current frame rate in frames / second.
|
||||
* Return false from function, if you don't need to redraw layer/layers on some frames.
|
||||
* @param {Konva.Layer|Array} [layers] layer(s) to be redrawn on each animation frame. Can be a layer, an array of layers, or null.
|
||||
* Not specifying a node will result in no redraw.
|
||||
* @example
|
||||
* // move a node to the right at 50 pixels / second
|
||||
* var velocity = 50;
|
||||
*
|
||||
* var anim = new Konva.Animation(function(frame) {
|
||||
* var dist = velocity * (frame.timeDiff / 1000);
|
||||
* node.move({x: dist, y: 0});
|
||||
* }, layer);
|
||||
*
|
||||
* anim.start();
|
||||
*/
|
||||
export class Animation {
|
||||
constructor(func, layers) {
|
||||
this.id = Animation.animIdCounter++;
|
||||
this.frame = {
|
||||
time: 0,
|
||||
timeDiff: 0,
|
||||
lastTime: now(),
|
||||
frameRate: 0,
|
||||
};
|
||||
this.func = func;
|
||||
this.setLayers(layers);
|
||||
}
|
||||
/**
|
||||
* set layers to be redrawn on each animation frame
|
||||
* @method
|
||||
* @name Konva.Animation#setLayers
|
||||
* @param {Konva.Layer|Array} [layers] layer(s) to be redrawn. Can be a layer, an array of layers, or null. Not specifying a node will result in no redraw.
|
||||
* @return {Konva.Animation} this
|
||||
*/
|
||||
setLayers(layers) {
|
||||
// a copy, so that addLayer() never pushes into the caller's array
|
||||
this.layers = layers ? [].concat(layers) : [];
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* get layers
|
||||
* @method
|
||||
* @name Konva.Animation#getLayers
|
||||
* @return {Array} Array of Konva.Layer
|
||||
*/
|
||||
getLayers() {
|
||||
return this.layers;
|
||||
}
|
||||
/**
|
||||
* add layer. Returns true if the layer was added, and false if it was not
|
||||
* @method
|
||||
* @name Konva.Animation#addLayer
|
||||
* @param {Konva.Layer} layer to add
|
||||
* @return {Bool} true if layer is added to animation, otherwise false
|
||||
*/
|
||||
addLayer(layer) {
|
||||
const layers = this.layers;
|
||||
const len = layers.length;
|
||||
// don't add the layer if it already exists
|
||||
for (let n = 0; n < len; n++) {
|
||||
if (layers[n]._id === layer._id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this.layers.push(layer);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* determine if animation is running or not. returns true or false
|
||||
* @method
|
||||
* @name Konva.Animation#isRunning
|
||||
* @return {Bool} is animation running?
|
||||
*/
|
||||
isRunning() {
|
||||
return Animation.animations.has(this);
|
||||
}
|
||||
/**
|
||||
* start animation
|
||||
* @method
|
||||
* @name Konva.Animation#start
|
||||
* @return {Konva.Animation} this
|
||||
*/
|
||||
start() {
|
||||
this.stop();
|
||||
this.frame.timeDiff = 0;
|
||||
this.frame.lastTime = now();
|
||||
Animation._addAnimation(this);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* stop animation
|
||||
* @method
|
||||
* @name Konva.Animation#stop
|
||||
* @return {Konva.Animation} this
|
||||
*/
|
||||
stop() {
|
||||
Animation._removeAnimation(this);
|
||||
return this;
|
||||
}
|
||||
_updateFrameObject(time) {
|
||||
this.frame.timeDiff = time - this.frame.lastTime;
|
||||
this.frame.lastTime = time;
|
||||
this.frame.time += this.frame.timeDiff;
|
||||
this.frame.frameRate = 1000 / this.frame.timeDiff;
|
||||
}
|
||||
static _addAnimation(anim) {
|
||||
this.animations.add(anim);
|
||||
this._handleAnimation();
|
||||
}
|
||||
static _removeAnimation(anim) {
|
||||
this.animations.delete(anim);
|
||||
}
|
||||
static _runFrames() {
|
||||
// every layer is drawn once, however many animations touch it
|
||||
const layersToDraw = new Set();
|
||||
// an animation may stop or restart itself, or another one, inside its
|
||||
// function: each one runs at most once per frame, a stopped one not at all
|
||||
Node._updateBatch(() => {
|
||||
Array.from(this.animations).forEach((anim) => {
|
||||
if (!this.animations.has(anim)) {
|
||||
return;
|
||||
}
|
||||
anim._updateFrameObject(now());
|
||||
// the function returns false to skip the redraw
|
||||
if (anim.func && anim.func.call(anim, anim.frame) === false) {
|
||||
return;
|
||||
}
|
||||
anim.layers.forEach((layer) => layer && layersToDraw.add(layer));
|
||||
});
|
||||
});
|
||||
layersToDraw.forEach((layer) => layer.batchDraw());
|
||||
}
|
||||
static _animationLoop() {
|
||||
const Anim = Animation;
|
||||
if (Anim.animations.size) {
|
||||
Anim._runFrames();
|
||||
Util.requestAnimFrame(Anim._animationLoop);
|
||||
}
|
||||
else {
|
||||
Anim.animRunning = false;
|
||||
}
|
||||
}
|
||||
static _handleAnimation() {
|
||||
if (!this.animRunning) {
|
||||
this.animRunning = true;
|
||||
Util.requestAnimFrame(this._animationLoop);
|
||||
}
|
||||
}
|
||||
}
|
||||
Animation.animations = new Set();
|
||||
Animation.animIdCounter = 0;
|
||||
Animation.animRunning = false;
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
export declare const tValues: number[][];
|
||||
export declare const cValues: number[][];
|
||||
export declare const binomialCoefficients: number[][];
|
||||
export declare const getCubicArcLength: (xs: number[], ys: number[], t: number) => number;
|
||||
export declare const getQuadraticArcLength: (xs: number[], ys: number[], t?: number) => number;
|
||||
export declare const t2length: (length: number, totalLength: number, func: (t: number) => number) => number;
|
||||
/**
|
||||
* Points where a quadratic bezier segment reaches its highest or lowest value
|
||||
* on one of the two axes, as a flat [x, y, x, y, ...] list. The two end points
|
||||
* are not included, because the caller knows them already.
|
||||
*/
|
||||
export declare const getQuadraticExtremaPoints: (x0: number, y0: number, x1: number, y1: number, x2: number, y2: number) => number[];
|
||||
/**
|
||||
* Points where a cubic bezier segment reaches its highest or lowest value on
|
||||
* one of the two axes, as a flat [x, y, x, y, ...] list. The two end points
|
||||
* are not included, because the caller knows them already.
|
||||
*/
|
||||
export declare const getCubicExtremaPoints: (x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number) => number[];
|
||||
+822
@@ -0,0 +1,822 @@
|
||||
// Credits: rveciana/svg-path-properties
|
||||
// Keep order 20 separate so the main bundle can omit the other quadrature rows.
|
||||
// Legendre-Gauss abscissae (xi values, defined at i=n as the roots of the nth order Legendre polynomial Pn(x))
|
||||
const tValues20 = [
|
||||
-0.0765265211334973337546404093988382110047,
|
||||
0.0765265211334973337546404093988382110047,
|
||||
-0.227785851141645078080496195368574624743,
|
||||
0.227785851141645078080496195368574624743,
|
||||
-0.3737060887154195606725481770249272373957,
|
||||
0.3737060887154195606725481770249272373957,
|
||||
-0.5108670019508270980043640509552509984254,
|
||||
0.5108670019508270980043640509552509984254,
|
||||
-0.6360536807265150254528366962262859367433,
|
||||
0.6360536807265150254528366962262859367433,
|
||||
-0.7463319064601507926143050703556415903107,
|
||||
0.7463319064601507926143050703556415903107,
|
||||
-0.8391169718222188233945290617015206853296,
|
||||
0.8391169718222188233945290617015206853296,
|
||||
-0.9122344282513259058677524412032981130491,
|
||||
0.9122344282513259058677524412032981130491,
|
||||
-0.963971927277913791267666131197277221912,
|
||||
0.963971927277913791267666131197277221912,
|
||||
-0.9931285991850949247861223884713202782226,
|
||||
0.9931285991850949247861223884713202782226,
|
||||
];
|
||||
export const tValues = [
|
||||
[],
|
||||
[],
|
||||
[
|
||||
-0.5773502691896257645091487805019574556476,
|
||||
0.5773502691896257645091487805019574556476,
|
||||
],
|
||||
[
|
||||
0, -0.7745966692414833770358530799564799221665,
|
||||
0.7745966692414833770358530799564799221665,
|
||||
],
|
||||
[
|
||||
-0.3399810435848562648026657591032446872005,
|
||||
0.3399810435848562648026657591032446872005,
|
||||
-0.8611363115940525752239464888928095050957,
|
||||
0.8611363115940525752239464888928095050957,
|
||||
],
|
||||
[
|
||||
0, -0.5384693101056830910363144207002088049672,
|
||||
0.5384693101056830910363144207002088049672,
|
||||
-0.9061798459386639927976268782993929651256,
|
||||
0.9061798459386639927976268782993929651256,
|
||||
],
|
||||
[
|
||||
0.6612093864662645136613995950199053470064,
|
||||
-0.6612093864662645136613995950199053470064,
|
||||
-0.2386191860831969086305017216807119354186,
|
||||
0.2386191860831969086305017216807119354186,
|
||||
-0.9324695142031520278123015544939946091347,
|
||||
0.9324695142031520278123015544939946091347,
|
||||
],
|
||||
[
|
||||
0, 0.4058451513773971669066064120769614633473,
|
||||
-0.4058451513773971669066064120769614633473,
|
||||
-0.7415311855993944398638647732807884070741,
|
||||
0.7415311855993944398638647732807884070741,
|
||||
-0.9491079123427585245261896840478512624007,
|
||||
0.9491079123427585245261896840478512624007,
|
||||
],
|
||||
[
|
||||
-0.1834346424956498049394761423601839806667,
|
||||
0.1834346424956498049394761423601839806667,
|
||||
-0.5255324099163289858177390491892463490419,
|
||||
0.5255324099163289858177390491892463490419,
|
||||
-0.7966664774136267395915539364758304368371,
|
||||
0.7966664774136267395915539364758304368371,
|
||||
-0.9602898564975362316835608685694729904282,
|
||||
0.9602898564975362316835608685694729904282,
|
||||
],
|
||||
[
|
||||
0, -0.8360311073266357942994297880697348765441,
|
||||
0.8360311073266357942994297880697348765441,
|
||||
-0.9681602395076260898355762029036728700494,
|
||||
0.9681602395076260898355762029036728700494,
|
||||
-0.3242534234038089290385380146433366085719,
|
||||
0.3242534234038089290385380146433366085719,
|
||||
-0.6133714327005903973087020393414741847857,
|
||||
0.6133714327005903973087020393414741847857,
|
||||
],
|
||||
[
|
||||
-0.1488743389816312108848260011297199846175,
|
||||
0.1488743389816312108848260011297199846175,
|
||||
-0.4333953941292471907992659431657841622,
|
||||
0.4333953941292471907992659431657841622,
|
||||
-0.6794095682990244062343273651148735757692,
|
||||
0.6794095682990244062343273651148735757692,
|
||||
-0.8650633666889845107320966884234930485275,
|
||||
0.8650633666889845107320966884234930485275,
|
||||
-0.9739065285171717200779640120844520534282,
|
||||
0.9739065285171717200779640120844520534282,
|
||||
],
|
||||
[
|
||||
0, -0.2695431559523449723315319854008615246796,
|
||||
0.2695431559523449723315319854008615246796,
|
||||
-0.5190961292068118159257256694586095544802,
|
||||
0.5190961292068118159257256694586095544802,
|
||||
-0.7301520055740493240934162520311534580496,
|
||||
0.7301520055740493240934162520311534580496,
|
||||
-0.8870625997680952990751577693039272666316,
|
||||
0.8870625997680952990751577693039272666316,
|
||||
-0.9782286581460569928039380011228573907714,
|
||||
0.9782286581460569928039380011228573907714,
|
||||
],
|
||||
[
|
||||
-0.1252334085114689154724413694638531299833,
|
||||
0.1252334085114689154724413694638531299833,
|
||||
-0.3678314989981801937526915366437175612563,
|
||||
0.3678314989981801937526915366437175612563,
|
||||
-0.587317954286617447296702418940534280369,
|
||||
0.587317954286617447296702418940534280369,
|
||||
-0.7699026741943046870368938332128180759849,
|
||||
0.7699026741943046870368938332128180759849,
|
||||
-0.9041172563704748566784658661190961925375,
|
||||
0.9041172563704748566784658661190961925375,
|
||||
-0.9815606342467192506905490901492808229601,
|
||||
0.9815606342467192506905490901492808229601,
|
||||
],
|
||||
[
|
||||
0, -0.2304583159551347940655281210979888352115,
|
||||
0.2304583159551347940655281210979888352115,
|
||||
-0.4484927510364468528779128521276398678019,
|
||||
0.4484927510364468528779128521276398678019,
|
||||
-0.6423493394403402206439846069955156500716,
|
||||
0.6423493394403402206439846069955156500716,
|
||||
-0.8015780907333099127942064895828598903056,
|
||||
0.8015780907333099127942064895828598903056,
|
||||
-0.9175983992229779652065478365007195123904,
|
||||
0.9175983992229779652065478365007195123904,
|
||||
-0.9841830547185881494728294488071096110649,
|
||||
0.9841830547185881494728294488071096110649,
|
||||
],
|
||||
[
|
||||
-0.1080549487073436620662446502198347476119,
|
||||
0.1080549487073436620662446502198347476119,
|
||||
-0.3191123689278897604356718241684754668342,
|
||||
0.3191123689278897604356718241684754668342,
|
||||
-0.5152486363581540919652907185511886623088,
|
||||
0.5152486363581540919652907185511886623088,
|
||||
-0.6872929048116854701480198030193341375384,
|
||||
0.6872929048116854701480198030193341375384,
|
||||
-0.8272013150697649931897947426503949610397,
|
||||
0.8272013150697649931897947426503949610397,
|
||||
-0.928434883663573517336391139377874264477,
|
||||
0.928434883663573517336391139377874264477,
|
||||
-0.986283808696812338841597266704052801676,
|
||||
0.986283808696812338841597266704052801676,
|
||||
],
|
||||
[
|
||||
0, -0.2011940939974345223006283033945962078128,
|
||||
0.2011940939974345223006283033945962078128,
|
||||
-0.3941513470775633698972073709810454683627,
|
||||
0.3941513470775633698972073709810454683627,
|
||||
-0.5709721726085388475372267372539106412383,
|
||||
0.5709721726085388475372267372539106412383,
|
||||
-0.7244177313601700474161860546139380096308,
|
||||
0.7244177313601700474161860546139380096308,
|
||||
-0.8482065834104272162006483207742168513662,
|
||||
0.8482065834104272162006483207742168513662,
|
||||
-0.9372733924007059043077589477102094712439,
|
||||
0.9372733924007059043077589477102094712439,
|
||||
-0.9879925180204854284895657185866125811469,
|
||||
0.9879925180204854284895657185866125811469,
|
||||
],
|
||||
[
|
||||
-0.0950125098376374401853193354249580631303,
|
||||
0.0950125098376374401853193354249580631303,
|
||||
-0.281603550779258913230460501460496106486,
|
||||
0.281603550779258913230460501460496106486,
|
||||
-0.45801677765722738634241944298357757354,
|
||||
0.45801677765722738634241944298357757354,
|
||||
-0.6178762444026437484466717640487910189918,
|
||||
0.6178762444026437484466717640487910189918,
|
||||
-0.7554044083550030338951011948474422683538,
|
||||
0.7554044083550030338951011948474422683538,
|
||||
-0.8656312023878317438804678977123931323873,
|
||||
0.8656312023878317438804678977123931323873,
|
||||
-0.9445750230732325760779884155346083450911,
|
||||
0.9445750230732325760779884155346083450911,
|
||||
-0.9894009349916499325961541734503326274262,
|
||||
0.9894009349916499325961541734503326274262,
|
||||
],
|
||||
[
|
||||
0, -0.1784841814958478558506774936540655574754,
|
||||
0.1784841814958478558506774936540655574754,
|
||||
-0.3512317634538763152971855170953460050405,
|
||||
0.3512317634538763152971855170953460050405,
|
||||
-0.5126905370864769678862465686295518745829,
|
||||
0.5126905370864769678862465686295518745829,
|
||||
-0.6576711592166907658503022166430023351478,
|
||||
0.6576711592166907658503022166430023351478,
|
||||
-0.7815140038968014069252300555204760502239,
|
||||
0.7815140038968014069252300555204760502239,
|
||||
-0.8802391537269859021229556944881556926234,
|
||||
0.8802391537269859021229556944881556926234,
|
||||
-0.9506755217687677612227169578958030214433,
|
||||
0.9506755217687677612227169578958030214433,
|
||||
-0.9905754753144173356754340199406652765077,
|
||||
0.9905754753144173356754340199406652765077,
|
||||
],
|
||||
[
|
||||
-0.0847750130417353012422618529357838117333,
|
||||
0.0847750130417353012422618529357838117333,
|
||||
-0.2518862256915055095889728548779112301628,
|
||||
0.2518862256915055095889728548779112301628,
|
||||
-0.4117511614628426460359317938330516370789,
|
||||
0.4117511614628426460359317938330516370789,
|
||||
-0.5597708310739475346078715485253291369276,
|
||||
0.5597708310739475346078715485253291369276,
|
||||
-0.6916870430603532078748910812888483894522,
|
||||
0.6916870430603532078748910812888483894522,
|
||||
-0.8037049589725231156824174550145907971032,
|
||||
0.8037049589725231156824174550145907971032,
|
||||
-0.8926024664975557392060605911271455154078,
|
||||
0.8926024664975557392060605911271455154078,
|
||||
-0.9558239495713977551811958929297763099728,
|
||||
0.9558239495713977551811958929297763099728,
|
||||
-0.9915651684209309467300160047061507702525,
|
||||
0.9915651684209309467300160047061507702525,
|
||||
],
|
||||
[
|
||||
0, -0.1603586456402253758680961157407435495048,
|
||||
0.1603586456402253758680961157407435495048,
|
||||
-0.3165640999636298319901173288498449178922,
|
||||
0.3165640999636298319901173288498449178922,
|
||||
-0.4645707413759609457172671481041023679762,
|
||||
0.4645707413759609457172671481041023679762,
|
||||
-0.6005453046616810234696381649462392798683,
|
||||
0.6005453046616810234696381649462392798683,
|
||||
-0.7209661773352293786170958608237816296571,
|
||||
0.7209661773352293786170958608237816296571,
|
||||
-0.8227146565371428249789224867127139017745,
|
||||
0.8227146565371428249789224867127139017745,
|
||||
-0.9031559036148179016426609285323124878093,
|
||||
0.9031559036148179016426609285323124878093,
|
||||
-0.960208152134830030852778840687651526615,
|
||||
0.960208152134830030852778840687651526615,
|
||||
-0.9924068438435844031890176702532604935893,
|
||||
0.9924068438435844031890176702532604935893,
|
||||
],
|
||||
tValues20,
|
||||
[
|
||||
0, -0.1455618541608950909370309823386863301163,
|
||||
0.1455618541608950909370309823386863301163,
|
||||
-0.288021316802401096600792516064600319909,
|
||||
0.288021316802401096600792516064600319909,
|
||||
-0.4243421202074387835736688885437880520964,
|
||||
0.4243421202074387835736688885437880520964,
|
||||
-0.551618835887219807059018796724313286622,
|
||||
0.551618835887219807059018796724313286622,
|
||||
-0.667138804197412319305966669990339162597,
|
||||
0.667138804197412319305966669990339162597,
|
||||
-0.7684399634756779086158778513062280348209,
|
||||
0.7684399634756779086158778513062280348209,
|
||||
-0.8533633645833172836472506385875676702761,
|
||||
0.8533633645833172836472506385875676702761,
|
||||
-0.9200993341504008287901871337149688941591,
|
||||
0.9200993341504008287901871337149688941591,
|
||||
-0.9672268385663062943166222149076951614246,
|
||||
0.9672268385663062943166222149076951614246,
|
||||
-0.9937521706203895002602420359379409291933,
|
||||
0.9937521706203895002602420359379409291933,
|
||||
],
|
||||
[
|
||||
-0.0697392733197222212138417961186280818222,
|
||||
0.0697392733197222212138417961186280818222,
|
||||
-0.2078604266882212854788465339195457342156,
|
||||
0.2078604266882212854788465339195457342156,
|
||||
-0.3419358208920842251581474204273796195591,
|
||||
0.3419358208920842251581474204273796195591,
|
||||
-0.4693558379867570264063307109664063460953,
|
||||
0.4693558379867570264063307109664063460953,
|
||||
-0.5876404035069115929588769276386473488776,
|
||||
0.5876404035069115929588769276386473488776,
|
||||
-0.6944872631866827800506898357622567712673,
|
||||
0.6944872631866827800506898357622567712673,
|
||||
-0.7878168059792081620042779554083515213881,
|
||||
0.7878168059792081620042779554083515213881,
|
||||
-0.8658125777203001365364256370193787290847,
|
||||
0.8658125777203001365364256370193787290847,
|
||||
-0.9269567721871740005206929392590531966353,
|
||||
0.9269567721871740005206929392590531966353,
|
||||
-0.9700604978354287271239509867652687108059,
|
||||
0.9700604978354287271239509867652687108059,
|
||||
-0.994294585482399292073031421161298980393,
|
||||
0.994294585482399292073031421161298980393,
|
||||
],
|
||||
[
|
||||
0, -0.1332568242984661109317426822417661370104,
|
||||
0.1332568242984661109317426822417661370104,
|
||||
-0.264135680970344930533869538283309602979,
|
||||
0.264135680970344930533869538283309602979,
|
||||
-0.390301038030290831421488872880605458578,
|
||||
0.390301038030290831421488872880605458578,
|
||||
-0.5095014778460075496897930478668464305448,
|
||||
0.5095014778460075496897930478668464305448,
|
||||
-0.6196098757636461563850973116495956533871,
|
||||
0.6196098757636461563850973116495956533871,
|
||||
-0.7186613631319501944616244837486188483299,
|
||||
0.7186613631319501944616244837486188483299,
|
||||
-0.8048884016188398921511184069967785579414,
|
||||
0.8048884016188398921511184069967785579414,
|
||||
-0.8767523582704416673781568859341456716389,
|
||||
0.8767523582704416673781568859341456716389,
|
||||
-0.9329710868260161023491969890384229782357,
|
||||
0.9329710868260161023491969890384229782357,
|
||||
-0.9725424712181152319560240768207773751816,
|
||||
0.9725424712181152319560240768207773751816,
|
||||
-0.9947693349975521235239257154455743605736,
|
||||
0.9947693349975521235239257154455743605736,
|
||||
],
|
||||
[
|
||||
-0.0640568928626056260850430826247450385909,
|
||||
0.0640568928626056260850430826247450385909,
|
||||
-0.1911188674736163091586398207570696318404,
|
||||
0.1911188674736163091586398207570696318404,
|
||||
-0.3150426796961633743867932913198102407864,
|
||||
0.3150426796961633743867932913198102407864,
|
||||
-0.4337935076260451384870842319133497124524,
|
||||
0.4337935076260451384870842319133497124524,
|
||||
-0.5454214713888395356583756172183723700107,
|
||||
0.5454214713888395356583756172183723700107,
|
||||
-0.6480936519369755692524957869107476266696,
|
||||
0.6480936519369755692524957869107476266696,
|
||||
-0.7401241915785543642438281030999784255232,
|
||||
0.7401241915785543642438281030999784255232,
|
||||
-0.8200019859739029219539498726697452080761,
|
||||
0.8200019859739029219539498726697452080761,
|
||||
-0.8864155270044010342131543419821967550873,
|
||||
0.8864155270044010342131543419821967550873,
|
||||
-0.9382745520027327585236490017087214496548,
|
||||
0.9382745520027327585236490017087214496548,
|
||||
-0.9747285559713094981983919930081690617411,
|
||||
0.9747285559713094981983919930081690617411,
|
||||
-0.9951872199970213601799974097007368118745,
|
||||
0.9951872199970213601799974097007368118745,
|
||||
],
|
||||
];
|
||||
// Legendre-Gauss weights (wi values, defined by a function linked to in the Bezier primer article)
|
||||
const cValues20 = [
|
||||
0.1527533871307258506980843319550975934919,
|
||||
0.1527533871307258506980843319550975934919,
|
||||
0.1491729864726037467878287370019694366926,
|
||||
0.1491729864726037467878287370019694366926,
|
||||
0.1420961093183820513292983250671649330345,
|
||||
0.1420961093183820513292983250671649330345,
|
||||
0.1316886384491766268984944997481631349161,
|
||||
0.1316886384491766268984944997481631349161,
|
||||
0.118194531961518417312377377711382287005,
|
||||
0.118194531961518417312377377711382287005,
|
||||
0.1019301198172404350367501354803498761666,
|
||||
0.1019301198172404350367501354803498761666,
|
||||
0.0832767415767047487247581432220462061001,
|
||||
0.0832767415767047487247581432220462061001,
|
||||
0.0626720483341090635695065351870416063516,
|
||||
0.0626720483341090635695065351870416063516,
|
||||
0.040601429800386941331039952274932109879,
|
||||
0.040601429800386941331039952274932109879,
|
||||
0.0176140071391521183118619623518528163621,
|
||||
0.0176140071391521183118619623518528163621,
|
||||
];
|
||||
export const cValues = [
|
||||
[],
|
||||
[],
|
||||
[1.0, 1.0],
|
||||
[
|
||||
0.8888888888888888888888888888888888888888,
|
||||
0.5555555555555555555555555555555555555555,
|
||||
0.5555555555555555555555555555555555555555,
|
||||
],
|
||||
[
|
||||
0.6521451548625461426269360507780005927646,
|
||||
0.6521451548625461426269360507780005927646,
|
||||
0.3478548451374538573730639492219994072353,
|
||||
0.3478548451374538573730639492219994072353,
|
||||
],
|
||||
[
|
||||
0.5688888888888888888888888888888888888888,
|
||||
0.4786286704993664680412915148356381929122,
|
||||
0.4786286704993664680412915148356381929122,
|
||||
0.2369268850561890875142640407199173626432,
|
||||
0.2369268850561890875142640407199173626432,
|
||||
],
|
||||
[
|
||||
0.3607615730481386075698335138377161116615,
|
||||
0.3607615730481386075698335138377161116615,
|
||||
0.4679139345726910473898703439895509948116,
|
||||
0.4679139345726910473898703439895509948116,
|
||||
0.1713244923791703450402961421727328935268,
|
||||
0.1713244923791703450402961421727328935268,
|
||||
],
|
||||
[
|
||||
0.4179591836734693877551020408163265306122,
|
||||
0.3818300505051189449503697754889751338783,
|
||||
0.3818300505051189449503697754889751338783,
|
||||
0.2797053914892766679014677714237795824869,
|
||||
0.2797053914892766679014677714237795824869,
|
||||
0.1294849661688696932706114326790820183285,
|
||||
0.1294849661688696932706114326790820183285,
|
||||
],
|
||||
[
|
||||
0.3626837833783619829651504492771956121941,
|
||||
0.3626837833783619829651504492771956121941,
|
||||
0.3137066458778872873379622019866013132603,
|
||||
0.3137066458778872873379622019866013132603,
|
||||
0.2223810344533744705443559944262408844301,
|
||||
0.2223810344533744705443559944262408844301,
|
||||
0.1012285362903762591525313543099621901153,
|
||||
0.1012285362903762591525313543099621901153,
|
||||
],
|
||||
[
|
||||
0.3302393550012597631645250692869740488788,
|
||||
0.1806481606948574040584720312429128095143,
|
||||
0.1806481606948574040584720312429128095143,
|
||||
0.0812743883615744119718921581105236506756,
|
||||
0.0812743883615744119718921581105236506756,
|
||||
0.3123470770400028400686304065844436655987,
|
||||
0.3123470770400028400686304065844436655987,
|
||||
0.2606106964029354623187428694186328497718,
|
||||
0.2606106964029354623187428694186328497718,
|
||||
],
|
||||
[
|
||||
0.295524224714752870173892994651338329421,
|
||||
0.295524224714752870173892994651338329421,
|
||||
0.2692667193099963550912269215694693528597,
|
||||
0.2692667193099963550912269215694693528597,
|
||||
0.2190863625159820439955349342281631924587,
|
||||
0.2190863625159820439955349342281631924587,
|
||||
0.1494513491505805931457763396576973324025,
|
||||
0.1494513491505805931457763396576973324025,
|
||||
0.0666713443086881375935688098933317928578,
|
||||
0.0666713443086881375935688098933317928578,
|
||||
],
|
||||
[
|
||||
0.272925086777900630714483528336342189156,
|
||||
0.2628045445102466621806888698905091953727,
|
||||
0.2628045445102466621806888698905091953727,
|
||||
0.2331937645919904799185237048431751394317,
|
||||
0.2331937645919904799185237048431751394317,
|
||||
0.1862902109277342514260976414316558916912,
|
||||
0.1862902109277342514260976414316558916912,
|
||||
0.1255803694649046246346942992239401001976,
|
||||
0.1255803694649046246346942992239401001976,
|
||||
0.0556685671161736664827537204425485787285,
|
||||
0.0556685671161736664827537204425485787285,
|
||||
],
|
||||
[
|
||||
0.2491470458134027850005624360429512108304,
|
||||
0.2491470458134027850005624360429512108304,
|
||||
0.2334925365383548087608498989248780562594,
|
||||
0.2334925365383548087608498989248780562594,
|
||||
0.2031674267230659217490644558097983765065,
|
||||
0.2031674267230659217490644558097983765065,
|
||||
0.160078328543346226334652529543359071872,
|
||||
0.160078328543346226334652529543359071872,
|
||||
0.1069393259953184309602547181939962242145,
|
||||
0.1069393259953184309602547181939962242145,
|
||||
0.047175336386511827194615961485017060317,
|
||||
0.047175336386511827194615961485017060317,
|
||||
],
|
||||
[
|
||||
0.2325515532308739101945895152688359481566,
|
||||
0.2262831802628972384120901860397766184347,
|
||||
0.2262831802628972384120901860397766184347,
|
||||
0.2078160475368885023125232193060527633865,
|
||||
0.2078160475368885023125232193060527633865,
|
||||
0.1781459807619457382800466919960979955128,
|
||||
0.1781459807619457382800466919960979955128,
|
||||
0.1388735102197872384636017768688714676218,
|
||||
0.1388735102197872384636017768688714676218,
|
||||
0.0921214998377284479144217759537971209236,
|
||||
0.0921214998377284479144217759537971209236,
|
||||
0.0404840047653158795200215922009860600419,
|
||||
0.0404840047653158795200215922009860600419,
|
||||
],
|
||||
[
|
||||
0.2152638534631577901958764433162600352749,
|
||||
0.2152638534631577901958764433162600352749,
|
||||
0.2051984637212956039659240656612180557103,
|
||||
0.2051984637212956039659240656612180557103,
|
||||
0.1855383974779378137417165901251570362489,
|
||||
0.1855383974779378137417165901251570362489,
|
||||
0.1572031671581935345696019386238421566056,
|
||||
0.1572031671581935345696019386238421566056,
|
||||
0.1215185706879031846894148090724766259566,
|
||||
0.1215185706879031846894148090724766259566,
|
||||
0.0801580871597602098056332770628543095836,
|
||||
0.0801580871597602098056332770628543095836,
|
||||
0.0351194603317518630318328761381917806197,
|
||||
0.0351194603317518630318328761381917806197,
|
||||
],
|
||||
[
|
||||
0.2025782419255612728806201999675193148386,
|
||||
0.1984314853271115764561183264438393248186,
|
||||
0.1984314853271115764561183264438393248186,
|
||||
0.1861610000155622110268005618664228245062,
|
||||
0.1861610000155622110268005618664228245062,
|
||||
0.1662692058169939335532008604812088111309,
|
||||
0.1662692058169939335532008604812088111309,
|
||||
0.1395706779261543144478047945110283225208,
|
||||
0.1395706779261543144478047945110283225208,
|
||||
0.1071592204671719350118695466858693034155,
|
||||
0.1071592204671719350118695466858693034155,
|
||||
0.0703660474881081247092674164506673384667,
|
||||
0.0703660474881081247092674164506673384667,
|
||||
0.0307532419961172683546283935772044177217,
|
||||
0.0307532419961172683546283935772044177217,
|
||||
],
|
||||
[
|
||||
0.1894506104550684962853967232082831051469,
|
||||
0.1894506104550684962853967232082831051469,
|
||||
0.1826034150449235888667636679692199393835,
|
||||
0.1826034150449235888667636679692199393835,
|
||||
0.1691565193950025381893120790303599622116,
|
||||
0.1691565193950025381893120790303599622116,
|
||||
0.1495959888165767320815017305474785489704,
|
||||
0.1495959888165767320815017305474785489704,
|
||||
0.1246289712555338720524762821920164201448,
|
||||
0.1246289712555338720524762821920164201448,
|
||||
0.0951585116824927848099251076022462263552,
|
||||
0.0951585116824927848099251076022462263552,
|
||||
0.0622535239386478928628438369943776942749,
|
||||
0.0622535239386478928628438369943776942749,
|
||||
0.0271524594117540948517805724560181035122,
|
||||
0.0271524594117540948517805724560181035122,
|
||||
],
|
||||
[
|
||||
0.1794464703562065254582656442618856214487,
|
||||
0.1765627053669926463252709901131972391509,
|
||||
0.1765627053669926463252709901131972391509,
|
||||
0.1680041021564500445099706637883231550211,
|
||||
0.1680041021564500445099706637883231550211,
|
||||
0.1540457610768102880814315948019586119404,
|
||||
0.1540457610768102880814315948019586119404,
|
||||
0.1351363684685254732863199817023501973721,
|
||||
0.1351363684685254732863199817023501973721,
|
||||
0.1118838471934039710947883856263559267358,
|
||||
0.1118838471934039710947883856263559267358,
|
||||
0.0850361483171791808835353701910620738504,
|
||||
0.0850361483171791808835353701910620738504,
|
||||
0.0554595293739872011294401653582446605128,
|
||||
0.0554595293739872011294401653582446605128,
|
||||
0.0241483028685479319601100262875653246916,
|
||||
0.0241483028685479319601100262875653246916,
|
||||
],
|
||||
[
|
||||
0.1691423829631435918406564701349866103341,
|
||||
0.1691423829631435918406564701349866103341,
|
||||
0.1642764837458327229860537764659275904123,
|
||||
0.1642764837458327229860537764659275904123,
|
||||
0.1546846751262652449254180038363747721932,
|
||||
0.1546846751262652449254180038363747721932,
|
||||
0.1406429146706506512047313037519472280955,
|
||||
0.1406429146706506512047313037519472280955,
|
||||
0.1225552067114784601845191268002015552281,
|
||||
0.1225552067114784601845191268002015552281,
|
||||
0.1009420441062871655628139849248346070628,
|
||||
0.1009420441062871655628139849248346070628,
|
||||
0.0764257302548890565291296776166365256053,
|
||||
0.0764257302548890565291296776166365256053,
|
||||
0.0497145488949697964533349462026386416808,
|
||||
0.0497145488949697964533349462026386416808,
|
||||
0.0216160135264833103133427102664524693876,
|
||||
0.0216160135264833103133427102664524693876,
|
||||
],
|
||||
[
|
||||
0.1610544498487836959791636253209167350399,
|
||||
0.1589688433939543476499564394650472016787,
|
||||
0.1589688433939543476499564394650472016787,
|
||||
0.152766042065859666778855400897662998461,
|
||||
0.152766042065859666778855400897662998461,
|
||||
0.1426067021736066117757461094419029724756,
|
||||
0.1426067021736066117757461094419029724756,
|
||||
0.1287539625393362276755157848568771170558,
|
||||
0.1287539625393362276755157848568771170558,
|
||||
0.1115666455473339947160239016817659974813,
|
||||
0.1115666455473339947160239016817659974813,
|
||||
0.0914900216224499994644620941238396526609,
|
||||
0.0914900216224499994644620941238396526609,
|
||||
0.0690445427376412265807082580060130449618,
|
||||
0.0690445427376412265807082580060130449618,
|
||||
0.0448142267656996003328381574019942119517,
|
||||
0.0448142267656996003328381574019942119517,
|
||||
0.0194617882297264770363120414644384357529,
|
||||
0.0194617882297264770363120414644384357529,
|
||||
],
|
||||
cValues20,
|
||||
[
|
||||
0.1460811336496904271919851476833711882448,
|
||||
0.1445244039899700590638271665537525436099,
|
||||
0.1445244039899700590638271665537525436099,
|
||||
0.1398873947910731547221334238675831108927,
|
||||
0.1398873947910731547221334238675831108927,
|
||||
0.132268938633337461781052574496775604329,
|
||||
0.132268938633337461781052574496775604329,
|
||||
0.1218314160537285341953671771257335983563,
|
||||
0.1218314160537285341953671771257335983563,
|
||||
0.1087972991671483776634745780701056420336,
|
||||
0.1087972991671483776634745780701056420336,
|
||||
0.0934444234560338615532897411139320884835,
|
||||
0.0934444234560338615532897411139320884835,
|
||||
0.0761001136283793020170516533001831792261,
|
||||
0.0761001136283793020170516533001831792261,
|
||||
0.0571344254268572082836358264724479574912,
|
||||
0.0571344254268572082836358264724479574912,
|
||||
0.0369537897708524937999506682993296661889,
|
||||
0.0369537897708524937999506682993296661889,
|
||||
0.0160172282577743333242246168584710152658,
|
||||
0.0160172282577743333242246168584710152658,
|
||||
],
|
||||
[
|
||||
0.1392518728556319933754102483418099578739,
|
||||
0.1392518728556319933754102483418099578739,
|
||||
0.1365414983460151713525738312315173965863,
|
||||
0.1365414983460151713525738312315173965863,
|
||||
0.1311735047870623707329649925303074458757,
|
||||
0.1311735047870623707329649925303074458757,
|
||||
0.1232523768105124242855609861548144719594,
|
||||
0.1232523768105124242855609861548144719594,
|
||||
0.1129322960805392183934006074217843191142,
|
||||
0.1129322960805392183934006074217843191142,
|
||||
0.1004141444428809649320788378305362823508,
|
||||
0.1004141444428809649320788378305362823508,
|
||||
0.0859416062170677274144436813727028661891,
|
||||
0.0859416062170677274144436813727028661891,
|
||||
0.0697964684245204880949614189302176573987,
|
||||
0.0697964684245204880949614189302176573987,
|
||||
0.0522933351526832859403120512732112561121,
|
||||
0.0522933351526832859403120512732112561121,
|
||||
0.0337749015848141547933022468659129013491,
|
||||
0.0337749015848141547933022468659129013491,
|
||||
0.0146279952982722006849910980471854451902,
|
||||
0.0146279952982722006849910980471854451902,
|
||||
],
|
||||
[
|
||||
0.1336545721861061753514571105458443385831,
|
||||
0.132462039404696617371642464703316925805,
|
||||
0.132462039404696617371642464703316925805,
|
||||
0.1289057221880821499785953393997936532597,
|
||||
0.1289057221880821499785953393997936532597,
|
||||
0.1230490843067295304675784006720096548158,
|
||||
0.1230490843067295304675784006720096548158,
|
||||
0.1149966402224113649416435129339613014914,
|
||||
0.1149966402224113649416435129339613014914,
|
||||
0.1048920914645414100740861850147438548584,
|
||||
0.1048920914645414100740861850147438548584,
|
||||
0.0929157660600351474770186173697646486034,
|
||||
0.0929157660600351474770186173697646486034,
|
||||
0.0792814117767189549228925247420432269137,
|
||||
0.0792814117767189549228925247420432269137,
|
||||
0.0642324214085258521271696151589109980391,
|
||||
0.0642324214085258521271696151589109980391,
|
||||
0.0480376717310846685716410716320339965612,
|
||||
0.0480376717310846685716410716320339965612,
|
||||
0.0309880058569794443106942196418845053837,
|
||||
0.0309880058569794443106942196418845053837,
|
||||
0.0134118594871417720813094934586150649766,
|
||||
0.0134118594871417720813094934586150649766,
|
||||
],
|
||||
[
|
||||
0.1279381953467521569740561652246953718517,
|
||||
0.1279381953467521569740561652246953718517,
|
||||
0.1258374563468282961213753825111836887264,
|
||||
0.1258374563468282961213753825111836887264,
|
||||
0.121670472927803391204463153476262425607,
|
||||
0.121670472927803391204463153476262425607,
|
||||
0.1155056680537256013533444839067835598622,
|
||||
0.1155056680537256013533444839067835598622,
|
||||
0.1074442701159656347825773424466062227946,
|
||||
0.1074442701159656347825773424466062227946,
|
||||
0.0976186521041138882698806644642471544279,
|
||||
0.0976186521041138882698806644642471544279,
|
||||
0.086190161531953275917185202983742667185,
|
||||
0.086190161531953275917185202983742667185,
|
||||
0.0733464814110803057340336152531165181193,
|
||||
0.0733464814110803057340336152531165181193,
|
||||
0.0592985849154367807463677585001085845412,
|
||||
0.0592985849154367807463677585001085845412,
|
||||
0.0442774388174198061686027482113382288593,
|
||||
0.0442774388174198061686027482113382288593,
|
||||
0.0285313886289336631813078159518782864491,
|
||||
0.0285313886289336631813078159518782864491,
|
||||
0.0123412297999871995468056670700372915759,
|
||||
0.0123412297999871995468056670700372915759,
|
||||
],
|
||||
];
|
||||
// LUT for binomial coefficient arrays per curve order 'n'
|
||||
export const binomialCoefficients = [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1]];
|
||||
export const getCubicArcLength = (xs, ys, t) => {
|
||||
const z = t / 2;
|
||||
let sum = 0;
|
||||
for (let i = 0; i < tValues20.length; i++) {
|
||||
const correctedT = z * tValues20[i] + z;
|
||||
sum += cValues20[i] * BFunc(xs, ys, correctedT);
|
||||
}
|
||||
return z * sum;
|
||||
};
|
||||
// a quadratic elevated to a cubic is the same curve, and the cubic quadrature
|
||||
// is stable where the closed form for a quadratic cancels itself out
|
||||
export const getQuadraticArcLength = (xs, ys, t = 1) => getCubicArcLength([
|
||||
xs[0],
|
||||
xs[0] + (2 / 3) * (xs[1] - xs[0]),
|
||||
xs[2] + (2 / 3) * (xs[1] - xs[2]),
|
||||
xs[2],
|
||||
], [
|
||||
ys[0],
|
||||
ys[0] + (2 / 3) * (ys[1] - ys[0]),
|
||||
ys[2] + (2 / 3) * (ys[1] - ys[2]),
|
||||
ys[2],
|
||||
], t);
|
||||
function BFunc(xs, ys, t) {
|
||||
// Interpolate the three derivative control points of a cubic curve.
|
||||
const mt = 1 - t;
|
||||
const a = mt * mt;
|
||||
const b = 2 * mt * t;
|
||||
const c = t * t;
|
||||
const dx = a * (3 * (xs[1] - xs[0])) +
|
||||
b * (3 * (xs[2] - xs[1])) +
|
||||
c * (3 * (xs[3] - xs[2]));
|
||||
const dy = a * (3 * (ys[1] - ys[0])) +
|
||||
b * (3 * (ys[2] - ys[1])) +
|
||||
c * (3 * (ys[3] - ys[2]));
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
export const t2length = (length, totalLength, func) => {
|
||||
let error = 1;
|
||||
let t = length / totalLength;
|
||||
let step = (length - func(t)) / totalLength;
|
||||
let numIterations = 0;
|
||||
while (error > 0.001) {
|
||||
const increasedTLength = func(t + step);
|
||||
const increasedTError = Math.abs(length - increasedTLength) / totalLength;
|
||||
if (increasedTError < error) {
|
||||
error = increasedTError;
|
||||
t += step;
|
||||
}
|
||||
else {
|
||||
const decreasedTLength = func(t - step);
|
||||
const decreasedTError = Math.abs(length - decreasedTLength) / totalLength;
|
||||
if (decreasedTError < error) {
|
||||
error = decreasedTError;
|
||||
t -= step;
|
||||
}
|
||||
else {
|
||||
step /= 2;
|
||||
}
|
||||
}
|
||||
numIterations++;
|
||||
if (numIterations > 500) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return t;
|
||||
};
|
||||
const quadraticAt = (p0, p1, p2, t) => {
|
||||
const mt = 1 - t;
|
||||
return mt * mt * p0 + 2 * mt * t * p1 + t * t * p2;
|
||||
};
|
||||
/**
|
||||
* Points where a quadratic bezier segment reaches its highest or lowest value
|
||||
* on one of the two axes, as a flat [x, y, x, y, ...] list. The two end points
|
||||
* are not included, because the caller knows them already.
|
||||
*/
|
||||
export const getQuadraticExtremaPoints = (x0, y0, x1, y1, x2, y2) => {
|
||||
const extrema = [];
|
||||
for (const axis of [
|
||||
[x0, x1, x2],
|
||||
[y0, y1, y2],
|
||||
]) {
|
||||
// the derivative of the quadratic is linear, so it has one root. A straight
|
||||
// segment makes the denominator 0, and the division then gives a value that
|
||||
// is not in the range below.
|
||||
const t = (axis[0] - axis[1]) / (axis[0] - 2 * axis[1] + axis[2]);
|
||||
if (t > 0 && t < 1) {
|
||||
extrema.push(quadraticAt(x0, x1, x2, t), quadraticAt(y0, y1, y2, t));
|
||||
}
|
||||
}
|
||||
return extrema;
|
||||
};
|
||||
const cubicAt = (p0, p1, p2, p3, t) => {
|
||||
const mt = 1 - t;
|
||||
return (mt * mt * mt * p0 +
|
||||
3 * mt * mt * t * p1 +
|
||||
3 * mt * t * t * p2 +
|
||||
t * t * t * p3);
|
||||
};
|
||||
/**
|
||||
* Points where a cubic bezier segment reaches its highest or lowest value on
|
||||
* one of the two axes, as a flat [x, y, x, y, ...] list. The two end points
|
||||
* are not included, because the caller knows them already.
|
||||
*/
|
||||
export const getCubicExtremaPoints = (x0, y0, x1, y1, x2, y2, x3, y3) => {
|
||||
const extrema = [];
|
||||
for (const axis of [
|
||||
[x0, x1, x2, x3],
|
||||
[y0, y1, y2, y3],
|
||||
]) {
|
||||
// the derivative of the cubic is a * t * t + b * t + c
|
||||
const a = -3 * axis[0] + 9 * axis[1] - 9 * axis[2] + 3 * axis[3];
|
||||
const b = 6 * axis[0] - 12 * axis[1] + 6 * axis[2];
|
||||
const c = -3 * axis[0] + 3 * axis[1];
|
||||
const discriminant = b * b - 4 * a * c;
|
||||
if (discriminant < 0) {
|
||||
continue;
|
||||
}
|
||||
// stable form of the quadratic formula. The plain (-b +- d) / (2 * a)
|
||||
// loses the root that stays finite when a is zero or near zero, which is
|
||||
// what a symmetric pair of control points gives. Roots that are not
|
||||
// finite fall out of the range test below.
|
||||
const q = -(b + (b < 0 ? -1 : 1) * Math.sqrt(discriminant)) / 2;
|
||||
for (const t of [q / a, c / q]) {
|
||||
if (t > 0 && t < 1) {
|
||||
extrema.push(cubicAt(x0, x1, x2, x3, t), cubicAt(y0, y1, y2, y3, t));
|
||||
}
|
||||
}
|
||||
}
|
||||
return extrema;
|
||||
};
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
import type { IRect } from './types.ts';
|
||||
import type { Context } from './Context.ts';
|
||||
interface ICanvasConfig {
|
||||
width?: number;
|
||||
height?: number;
|
||||
pixelRatio?: number;
|
||||
willReadFrequently?: boolean;
|
||||
}
|
||||
/**
|
||||
* Canvas Renderer constructor. It is a wrapper around native canvas element.
|
||||
* Usually you don't need to use it manually.
|
||||
* @constructor
|
||||
* @abstract
|
||||
* @memberof Konva
|
||||
* @param {Object} config
|
||||
* @param {Number} config.width
|
||||
* @param {Number} config.height
|
||||
* @param {Number} config.pixelRatio
|
||||
*/
|
||||
export declare class Canvas {
|
||||
pixelRatio: number;
|
||||
_canvas: HTMLCanvasElement;
|
||||
context: Context;
|
||||
width: number;
|
||||
height: number;
|
||||
_logicalWidth: number;
|
||||
_logicalHeight: number;
|
||||
isCache: boolean;
|
||||
constructor(config: ICanvasConfig);
|
||||
/**
|
||||
* get canvas context
|
||||
* @method
|
||||
* @name Konva.Canvas#getContext
|
||||
* @returns {CanvasContext} context
|
||||
*/
|
||||
getContext(): Context;
|
||||
/**
|
||||
* get pixel ratio
|
||||
* @method
|
||||
* @name Konva.Canvas#getPixelRatio
|
||||
* @returns {Number} pixel ratio
|
||||
* @example
|
||||
* var pixelRatio = layer.getCanvas().getPixelRatio();
|
||||
*/
|
||||
getPixelRatio(): number;
|
||||
/**
|
||||
* set pixel ratio
|
||||
* KonvaJS automatically handles pixel ratio adustments in order to render crisp drawings
|
||||
* on all devices. Most desktops, low end tablets, and low end phones, have device pixel ratios
|
||||
* of 1. Some high end tablets and phones, like iPhones and iPads have a device pixel ratio
|
||||
* of 2. Some Macbook Pros, and iMacs also have a device pixel ratio of 2. Some high end Android devices have pixel
|
||||
* ratios of 2 or 3. Some browsers like Firefox allow you to configure the pixel ratio of the viewport. Unless otherwise
|
||||
* specificed, the pixel ratio will be defaulted to the actual device pixel ratio. You can override the device pixel
|
||||
* ratio for special situations, or, if you don't want the pixel ratio to be taken into account, you can set it to 1.
|
||||
* @method
|
||||
* @name Konva.Canvas#setPixelRatio
|
||||
* @param {Number} pixelRatio
|
||||
* @example
|
||||
* layer.getCanvas().setPixelRatio(3);
|
||||
*/
|
||||
setPixelRatio(pixelRatio: any): void;
|
||||
setWidth(width: any): void;
|
||||
setHeight(height: any): void;
|
||||
getWidth(): number;
|
||||
getHeight(): number;
|
||||
_bitmapSize(size: number): number;
|
||||
setSize(width: any, height: any): void;
|
||||
setSizeIfChanged(width: number, height: number): void;
|
||||
/**
|
||||
* to data url
|
||||
* @method
|
||||
* @name Konva.Canvas#toDataURL
|
||||
* @param {String} mimeType
|
||||
* @param {Number} quality between 0 and 1 for jpg mime types
|
||||
* @returns {String} data url string
|
||||
*/
|
||||
toDataURL(mimeType: any, quality: any): string;
|
||||
}
|
||||
export declare class SceneCanvas extends Canvas {
|
||||
private _isolationCanvas?;
|
||||
_isolationRect?: IRect;
|
||||
private _isolationPeak;
|
||||
private _isolationLowFrames;
|
||||
_prepareIsolationCanvas(rect?: IRect): SceneCanvas;
|
||||
_trimIsolationCanvas(): void;
|
||||
_releaseIsolationCanvas(): void;
|
||||
setSize(width: any, height: any): void;
|
||||
constructor(config?: ICanvasConfig);
|
||||
}
|
||||
export declare class HitCanvas extends Canvas {
|
||||
hitCanvas: boolean;
|
||||
constructor(config?: ICanvasConfig);
|
||||
}
|
||||
export {};
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
import { Transform, Util } from "./Util.js";
|
||||
import { SceneContext, HitContext } from "./Context.js";
|
||||
import { Konva } from "./Global.js";
|
||||
/**
|
||||
* Canvas Renderer constructor. It is a wrapper around native canvas element.
|
||||
* Usually you don't need to use it manually.
|
||||
* @constructor
|
||||
* @abstract
|
||||
* @memberof Konva
|
||||
* @param {Object} config
|
||||
* @param {Number} config.width
|
||||
* @param {Number} config.height
|
||||
* @param {Number} config.pixelRatio
|
||||
*/
|
||||
export class Canvas {
|
||||
constructor(config) {
|
||||
this.pixelRatio = 1;
|
||||
// the bitmap size, in device pixels
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
// the size the canvas was given, in CSS pixels; the bitmap truncates it
|
||||
this._logicalWidth = 0;
|
||||
this._logicalHeight = 0;
|
||||
this.isCache = false;
|
||||
const conf = config || {};
|
||||
// a Konva.pixelRatio reset to undefined or 0 means the device ratio
|
||||
const pixelRatio = conf.pixelRatio ||
|
||||
Konva.pixelRatio ||
|
||||
Konva._global.devicePixelRatio ||
|
||||
1;
|
||||
this.pixelRatio = pixelRatio;
|
||||
this._canvas = Util.createCanvasElement();
|
||||
// set inline styles
|
||||
this._canvas.style.padding = '0';
|
||||
this._canvas.style.margin = '0';
|
||||
this._canvas.style.border = '0';
|
||||
this._canvas.style.background = 'transparent';
|
||||
this._canvas.style.position = 'absolute';
|
||||
this._canvas.style.top = '0';
|
||||
this._canvas.style.left = '0';
|
||||
}
|
||||
/**
|
||||
* get canvas context
|
||||
* @method
|
||||
* @name Konva.Canvas#getContext
|
||||
* @returns {CanvasContext} context
|
||||
*/
|
||||
getContext() {
|
||||
return this.context;
|
||||
}
|
||||
/**
|
||||
* get pixel ratio
|
||||
* @method
|
||||
* @name Konva.Canvas#getPixelRatio
|
||||
* @returns {Number} pixel ratio
|
||||
* @example
|
||||
* var pixelRatio = layer.getCanvas().getPixelRatio();
|
||||
*/
|
||||
getPixelRatio() {
|
||||
return this.pixelRatio;
|
||||
}
|
||||
/**
|
||||
* set pixel ratio
|
||||
* KonvaJS automatically handles pixel ratio adustments in order to render crisp drawings
|
||||
* on all devices. Most desktops, low end tablets, and low end phones, have device pixel ratios
|
||||
* of 1. Some high end tablets and phones, like iPhones and iPads have a device pixel ratio
|
||||
* of 2. Some Macbook Pros, and iMacs also have a device pixel ratio of 2. Some high end Android devices have pixel
|
||||
* ratios of 2 or 3. Some browsers like Firefox allow you to configure the pixel ratio of the viewport. Unless otherwise
|
||||
* specificed, the pixel ratio will be defaulted to the actual device pixel ratio. You can override the device pixel
|
||||
* ratio for special situations, or, if you don't want the pixel ratio to be taken into account, you can set it to 1.
|
||||
* @method
|
||||
* @name Konva.Canvas#setPixelRatio
|
||||
* @param {Number} pixelRatio
|
||||
* @example
|
||||
* layer.getCanvas().setPixelRatio(3);
|
||||
*/
|
||||
setPixelRatio(pixelRatio) {
|
||||
this.pixelRatio = pixelRatio;
|
||||
this.setSize(this._logicalWidth, this._logicalHeight);
|
||||
}
|
||||
setWidth(width) {
|
||||
this.setSize(width, this._logicalHeight);
|
||||
}
|
||||
setHeight(height) {
|
||||
this.setSize(this._logicalWidth, height);
|
||||
}
|
||||
getWidth() {
|
||||
return this.width;
|
||||
}
|
||||
getHeight() {
|
||||
return this.height;
|
||||
}
|
||||
// the bitmap holds whole pixels: a fractional pixel ratio truncates, and
|
||||
// a size that does not match the bitmap would resample every draw of it
|
||||
_bitmapSize(size) {
|
||||
return Math.floor((size || 0) * this.pixelRatio);
|
||||
}
|
||||
setSize(width, height) {
|
||||
// a NaN or Infinity size would silently give an empty bitmap
|
||||
if (!isFinite(width !== null && width !== void 0 ? width : 0) || !isFinite(height !== null && height !== void 0 ? height : 0)) {
|
||||
Util.error(`Canvas size must be finite numbers, got ${width}x${height}. The canvas is left empty.`);
|
||||
width = height = 0;
|
||||
}
|
||||
width = width || 0;
|
||||
height = height || 0;
|
||||
const pixelRatio = this.pixelRatio;
|
||||
const context = this.getContext()._context;
|
||||
const imageSmoothingEnabled = context.imageSmoothingEnabled;
|
||||
this._logicalWidth = width;
|
||||
this._logicalHeight = height;
|
||||
// take into account pixel ratio. Assigning a dimension reallocates the
|
||||
// bitmap and resets the whole context state, so scale once after both
|
||||
this.width = this._canvas.width = this._bitmapSize(width);
|
||||
this.height = this._canvas.height = this._bitmapSize(height);
|
||||
this._canvas.style.width = width + 'px';
|
||||
this._canvas.style.height = height + 'px';
|
||||
context.scale(pixelRatio, pixelRatio);
|
||||
context.imageSmoothingEnabled = imageSmoothingEnabled;
|
||||
}
|
||||
// setSize() re-allocates and clears the canvas even for the same size,
|
||||
// so lazily sized canvases use this to stay untouched when nothing changed
|
||||
setSizeIfChanged(width, height) {
|
||||
if (this.width !== this._bitmapSize(width) ||
|
||||
this.height !== this._bitmapSize(height)) {
|
||||
this.setSize(width, height);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* to data url
|
||||
* @method
|
||||
* @name Konva.Canvas#toDataURL
|
||||
* @param {String} mimeType
|
||||
* @param {Number} quality between 0 and 1 for jpg mime types
|
||||
* @returns {String} data url string
|
||||
*/
|
||||
toDataURL(mimeType, quality) {
|
||||
try {
|
||||
// If the requested encoding fails, retry with the default PNG encoding.
|
||||
return this._canvas.toDataURL(mimeType, quality);
|
||||
}
|
||||
catch (e) {
|
||||
try {
|
||||
return this._canvas.toDataURL();
|
||||
}
|
||||
catch (err) {
|
||||
Util.error('Unable to get data URL. ' +
|
||||
err.message +
|
||||
' For more info read https://konvajs.org/docs/posts/Tainted_Canvas.html.');
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
export class SceneCanvas extends Canvas {
|
||||
// A cleared surface over `rect` (in the current drawing space), or over the
|
||||
// whole canvas, in this canvas's device pixels. Siblings reuse it; nested
|
||||
// groups borrow from the surface itself, so they cannot clear a parent's
|
||||
// unfinished image.
|
||||
_prepareIsolationCanvas(rect) {
|
||||
const { a, b, c, d, e, f } = this.getContext()._context.getTransform();
|
||||
const view = this._isolationRect || this;
|
||||
let x = 0, y = 0, width = view.width, height = view.height;
|
||||
if (rect) {
|
||||
const box = new Transform([a, b, c, d, e, f])._getTransformedRect(rect);
|
||||
// Round outwards, with a pixel for antialiasing.
|
||||
x = Math.max(0, Math.floor(box.x) - 1);
|
||||
y = Math.max(0, Math.floor(box.y) - 1);
|
||||
width = Math.min(width, Math.ceil(box.x + box.width) + 1) - x;
|
||||
height = Math.min(height, Math.ceil(box.y + box.height) + 1) - y;
|
||||
}
|
||||
if (!(width > 0 && height > 0)) {
|
||||
// An empty source must still composite for copy/destination-in/etc.
|
||||
x = y = 0;
|
||||
width = height = 1;
|
||||
}
|
||||
const surface = (this._isolationCanvas || (this._isolationCanvas = new SceneCanvas({
|
||||
width: 0,
|
||||
height: 0,
|
||||
pixelRatio: this.pixelRatio,
|
||||
})));
|
||||
if (surface.width < width || surface.height < height) {
|
||||
// Headroom on a short side, so a growing group does not reallocate
|
||||
// every frame.
|
||||
const ratio = this.pixelRatio;
|
||||
const grow = (need, have, max) => Math.min(max, Math.ceil((have < need ? Math.max(need, have * 1.5) : have) / ratio));
|
||||
surface.setSize(grow(width, surface.width, this._logicalWidth), grow(height, surface.height, this._logicalHeight));
|
||||
}
|
||||
surface._isolationRect = { x, y, width, height };
|
||||
const peak = surface._isolationPeak;
|
||||
peak.width = Math.max(peak.width, width);
|
||||
peak.height = Math.max(peak.height, height);
|
||||
const context = surface.getContext();
|
||||
context.setTransform(1, 0, 0, 1, 0, 0);
|
||||
context.clearRect(0, 0, width, height);
|
||||
context.setTransform(a, b, c, d, e - x, f - y);
|
||||
const parent = this.getContext();
|
||||
context.imageSmoothingEnabled = parent.imageSmoothingEnabled;
|
||||
context.imageSmoothingQuality = parent.imageSmoothingQuality;
|
||||
context.direction = parent.direction;
|
||||
return surface;
|
||||
}
|
||||
// After a frame, free surfaces that ten frames in a row did not use or used
|
||||
// a small part of. Content that changes size keeps its surface.
|
||||
_trimIsolationCanvas() {
|
||||
const surface = this._isolationCanvas;
|
||||
if (!surface)
|
||||
return;
|
||||
const peak = surface._isolationPeak;
|
||||
const low = surface.width * surface.height > 4 * peak.width * peak.height;
|
||||
surface._isolationLowFrames = low ? surface._isolationLowFrames + 1 : 0;
|
||||
if (surface._isolationLowFrames >= 10) {
|
||||
this._releaseIsolationCanvas();
|
||||
}
|
||||
else {
|
||||
peak.width = peak.height = 0;
|
||||
surface._trimIsolationCanvas();
|
||||
}
|
||||
}
|
||||
_releaseIsolationCanvas() {
|
||||
const surface = this._isolationCanvas;
|
||||
if (surface) {
|
||||
surface._releaseIsolationCanvas();
|
||||
Util.releaseCanvas(surface._canvas);
|
||||
this._isolationCanvas = undefined;
|
||||
}
|
||||
}
|
||||
setSize(width, height) {
|
||||
// A bitmap reset also resets its origin-clean state. A surface from the
|
||||
// previous lifetime may be tainted even after clearing its pixels.
|
||||
this._releaseIsolationCanvas();
|
||||
super.setSize(width, height);
|
||||
}
|
||||
constructor(config = { width: 0, height: 0, willReadFrequently: false }) {
|
||||
super(config);
|
||||
// The largest width and height requested since the last trim.
|
||||
this._isolationPeak = { width: 0, height: 0 };
|
||||
// Consecutive frames that used less than a quarter of the surface.
|
||||
this._isolationLowFrames = 0;
|
||||
this.context = new SceneContext(this, {
|
||||
willReadFrequently: config.willReadFrequently,
|
||||
});
|
||||
this.setSize(config.width, config.height);
|
||||
}
|
||||
}
|
||||
export class HitCanvas extends Canvas {
|
||||
constructor(config = { width: 0, height: 0 }) {
|
||||
super(config);
|
||||
this.hitCanvas = true;
|
||||
this.context = new HitContext(this);
|
||||
this.context.imageSmoothingEnabled = false;
|
||||
this.setSize(config.width, config.height);
|
||||
}
|
||||
}
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
import type { HitCanvas, SceneCanvas } from './Canvas.ts';
|
||||
import type { SceneContext } from './Context.ts';
|
||||
import type { GetClientRectConfig, NodeConfig } from './Node.ts';
|
||||
import { Node } from './Node.ts';
|
||||
import type { Shape } from './Shape.ts';
|
||||
import type { GetSet, IRect } from './types.ts';
|
||||
export type ClipFuncOutput = void | [Path2D | CanvasFillRule] | [Path2D, CanvasFillRule];
|
||||
export interface ContainerConfig extends NodeConfig {
|
||||
clearBeforeDraw?: boolean;
|
||||
clipFunc?: (ctx: SceneContext, container: Container) => ClipFuncOutput;
|
||||
clipX?: number;
|
||||
clipY?: number;
|
||||
clipWidth?: number;
|
||||
clipHeight?: number;
|
||||
}
|
||||
/**
|
||||
* Container constructor. Containers are used to contain nodes or other containers
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Node
|
||||
* @abstract
|
||||
* @param {Object} config
|
||||
* @@nodeParams
|
||||
* @@containerParams
|
||||
*/
|
||||
export declare abstract class Container<ChildType extends Node = Node, Config extends ContainerConfig = ContainerConfig> extends Node<Config> {
|
||||
children: Array<ChildType>;
|
||||
/**
|
||||
* returns an array of direct descendant nodes
|
||||
* @method
|
||||
* @name Konva.Container#getChildren
|
||||
* @param {Function} [filterFunc] filter function
|
||||
* @returns {Array}
|
||||
* @example
|
||||
* // get all children
|
||||
* var children = layer.getChildren();
|
||||
*
|
||||
* // get only circles
|
||||
* var circles = layer.getChildren(function(node){
|
||||
* return node.getClassName() === 'Circle';
|
||||
* });
|
||||
*/
|
||||
getChildren(filterFunc?: (item: Node) => boolean): ChildType[];
|
||||
/**
|
||||
* determine if node has children
|
||||
* @method
|
||||
* @name Konva.Container#hasChildren
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
hasChildren(): boolean;
|
||||
/**
|
||||
* remove all children. Children will be still in memory.
|
||||
* If you want to completely destroy all children please use "destroyChildren" method instead
|
||||
* @method
|
||||
* @name Konva.Container#removeChildren
|
||||
*/
|
||||
removeChildren(): this;
|
||||
/**
|
||||
* destroy all children nodes.
|
||||
* @method
|
||||
* @name Konva.Container#destroyChildren
|
||||
*/
|
||||
destroyChildren(): this;
|
||||
_clearChildren(dispose: 'remove' | 'destroy'): this;
|
||||
abstract _validateAdd(node: Node): void;
|
||||
/**
|
||||
* add a child and children into container
|
||||
* @name Konva.Container#add
|
||||
* @method
|
||||
* @param {...Konva.Node} children
|
||||
* @returns {Container}
|
||||
* @example
|
||||
* layer.add(rect);
|
||||
* layer.add(shape1, shape2, shape3);
|
||||
* // empty arrays are accepted, though each individual child must be defined
|
||||
* layer.add(...shapes);
|
||||
*/
|
||||
add(...children: ChildType[]): this;
|
||||
destroy(): this;
|
||||
/**
|
||||
* return an array of nodes that match the selector.
|
||||
* You can provide a string with '#' for id selections and '.' for name selections.
|
||||
* Or a function that will return true/false when a node is passed through. See example below.
|
||||
* With strings you can also select by type or class name. Pass multiple selectors
|
||||
* separated by a comma.
|
||||
* @method
|
||||
* @name Konva.Container#find
|
||||
* @param {String | Function} selector
|
||||
* @returns {Array}
|
||||
* @example
|
||||
*
|
||||
* Passing a string as a selector
|
||||
* // select node with id foo
|
||||
* var node = stage.find('#foo');
|
||||
*
|
||||
* // select nodes with name bar inside layer
|
||||
* var nodes = layer.find('.bar');
|
||||
*
|
||||
* // select all groups inside layer
|
||||
* var nodes = layer.find('Group');
|
||||
*
|
||||
* // select all rectangles inside layer
|
||||
* var nodes = layer.find('Rect');
|
||||
*
|
||||
* // select node with an id of foo or a name of bar inside layer
|
||||
* var nodes = layer.find('#foo, .bar');
|
||||
*
|
||||
* Passing a function as a selector
|
||||
*
|
||||
* // get all groups with a function
|
||||
* var groups = stage.find(node => {
|
||||
* return node.getType() === 'Group';
|
||||
* });
|
||||
*
|
||||
* // get only Nodes with partial opacity
|
||||
* var alphaNodes = layer.find(node => {
|
||||
* return node.getType() === 'Node' && node.getAbsoluteOpacity() < 1;
|
||||
* });
|
||||
*/
|
||||
find<ChildNode extends Node>(selector: any): Array<ChildNode>;
|
||||
/**
|
||||
* return a first node from `find` method
|
||||
* @method
|
||||
* @name Konva.Container#findOne
|
||||
* @param {String | Function} selector
|
||||
* @returns {Konva.Node | Undefined}
|
||||
* @example
|
||||
* // select node with id foo
|
||||
* var node = stage.findOne('#foo');
|
||||
*
|
||||
* // select node with name bar inside layer
|
||||
* var nodes = layer.findOne('.bar');
|
||||
*
|
||||
* // select the first node to return true in a function
|
||||
* var node = stage.findOne(node => {
|
||||
* return node.getType() === 'Shape'
|
||||
* })
|
||||
*/
|
||||
findOne<ChildNode extends Node = Node>(selector: string | Function): ChildNode | undefined;
|
||||
_generalFind<ChildNode extends Node>(selector: string | Function, findOne: boolean): ChildNode[];
|
||||
private _descendants;
|
||||
toObject(): {
|
||||
attrs: any;
|
||||
className: string;
|
||||
children?: Array<any>;
|
||||
};
|
||||
/**
|
||||
* determine if node is an ancestor
|
||||
* of descendant
|
||||
* @method
|
||||
* @name Konva.Container#isAncestorOf
|
||||
* @param {Konva.Node} node
|
||||
*/
|
||||
isAncestorOf(node: Node): boolean;
|
||||
clone(obj?: any): this;
|
||||
/**
|
||||
* get all shapes that intersect a point. Note: because this method must clear a temporary
|
||||
* canvas and redraw every shape inside the container, it should only be used for special situations
|
||||
* because it performs very poorly. Please use the {@link Konva.Stage#getIntersection} method if at all possible
|
||||
* because it performs much better.
|
||||
* Nodes with listening set to false or invisible nodes are not detected
|
||||
* @method
|
||||
* @name Konva.Container#getAllIntersections
|
||||
* @param {Object} pos
|
||||
* @param {Number} pos.x
|
||||
* @param {Number} pos.y
|
||||
* @returns {Array} array of shapes
|
||||
*/
|
||||
getAllIntersections(pos: any): Shape<import("./Shape.ts").ShapeConfig>[];
|
||||
_clearSelfAndDescendantCache(attr?: string): void;
|
||||
_setChildrenIndices(): void;
|
||||
drawScene(can?: SceneCanvas, top?: Node): this;
|
||||
drawHit(can?: HitCanvas, top?: Node): this;
|
||||
_drawChildren(drawMethod: any, canvas: any, top: any): void;
|
||||
_drawChildNodes(drawMethod: any, canvas: any, top?: Node): void;
|
||||
getClientRect(config?: GetClientRectConfig): IRect;
|
||||
clip: GetSet<IRect, this>;
|
||||
clipX: GetSet<number, this>;
|
||||
clipY: GetSet<number, this>;
|
||||
clipWidth: GetSet<number, this>;
|
||||
clipHeight: GetSet<number, this>;
|
||||
clipFunc: GetSet<(ctx: SceneContext, container: Container) => ClipFuncOutput, this>;
|
||||
}
|
||||
/**
|
||||
* get/set clip function
|
||||
* @name Konva.Container#clipFunc
|
||||
* @method
|
||||
* @param {Function} function
|
||||
* @returns {Function}
|
||||
* @example
|
||||
* // get clip function
|
||||
* var clipFunction = container.clipFunc();
|
||||
*
|
||||
* // set clip function
|
||||
* container.clipFunc(function(ctx) {
|
||||
* ctx.rect(0, 0, 100, 100);
|
||||
* });
|
||||
*
|
||||
* container.clipFunc(function(ctx) {
|
||||
* // optionally return a clip Path2D and clip-rule or just the clip-rule
|
||||
* return [new Path2D('M0 0v50h50Z'), 'evenodd']
|
||||
* });
|
||||
*/
|
||||
+573
@@ -0,0 +1,573 @@
|
||||
import { Factory } from "./Factory.js";
|
||||
import { Node } from "./Node.js";
|
||||
import { getNumberValidator } from "./Validators.js";
|
||||
/**
|
||||
* Container constructor. Containers are used to contain nodes or other containers
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Node
|
||||
* @abstract
|
||||
* @param {Object} config
|
||||
* @@nodeParams
|
||||
* @@containerParams
|
||||
*/
|
||||
export class Container extends Node {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.children = [];
|
||||
}
|
||||
/**
|
||||
* returns an array of direct descendant nodes
|
||||
* @method
|
||||
* @name Konva.Container#getChildren
|
||||
* @param {Function} [filterFunc] filter function
|
||||
* @returns {Array}
|
||||
* @example
|
||||
* // get all children
|
||||
* var children = layer.getChildren();
|
||||
*
|
||||
* // get only circles
|
||||
* var circles = layer.getChildren(function(node){
|
||||
* return node.getClassName() === 'Circle';
|
||||
* });
|
||||
*/
|
||||
getChildren(filterFunc) {
|
||||
// a copy, so that removing children while iterating over it is safe
|
||||
const children = this.children || [];
|
||||
return filterFunc ? children.filter(filterFunc) : children.slice();
|
||||
}
|
||||
/**
|
||||
* determine if node has children
|
||||
* @method
|
||||
* @name Konva.Container#hasChildren
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
hasChildren() {
|
||||
return this.children.length > 0;
|
||||
}
|
||||
/**
|
||||
* remove all children. Children will be still in memory.
|
||||
* If you want to completely destroy all children please use "destroyChildren" method instead
|
||||
* @method
|
||||
* @name Konva.Container#removeChildren
|
||||
*/
|
||||
removeChildren() {
|
||||
return this._clearChildren('remove');
|
||||
}
|
||||
/**
|
||||
* destroy all children nodes.
|
||||
* @method
|
||||
* @name Konva.Container#destroyChildren
|
||||
*/
|
||||
destroyChildren() {
|
||||
return this._clearChildren('destroy');
|
||||
}
|
||||
_clearChildren(dispose) {
|
||||
return Node._updateBatch(() => {
|
||||
this.children.forEach((child) => {
|
||||
// reset parent to prevent many _setChildrenIndices calls
|
||||
child.parent = null;
|
||||
child.index = 0;
|
||||
child[dispose]();
|
||||
});
|
||||
this.children = [];
|
||||
// because all children were detached from parent, request draw via container
|
||||
this._notifySubtreeChange();
|
||||
this._requestDraw();
|
||||
return this;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* add a child and children into container
|
||||
* @name Konva.Container#add
|
||||
* @method
|
||||
* @param {...Konva.Node} children
|
||||
* @returns {Container}
|
||||
* @example
|
||||
* layer.add(rect);
|
||||
* layer.add(shape1, shape2, shape3);
|
||||
* // empty arrays are accepted, though each individual child must be defined
|
||||
* layer.add(...shapes);
|
||||
*/
|
||||
add(...children) {
|
||||
if (children.length === 0) {
|
||||
return this;
|
||||
}
|
||||
if (children.length > 1) {
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
this.add(children[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
const child = children[0];
|
||||
if (child.getParent()) {
|
||||
child.moveTo(this);
|
||||
return this;
|
||||
}
|
||||
this._validateAdd(child);
|
||||
child.index = this.children.length;
|
||||
child.parent = this;
|
||||
child._clearCaches();
|
||||
this.children.push(child);
|
||||
this._fire('add', {
|
||||
child: child,
|
||||
});
|
||||
this._notifySubtreeChange();
|
||||
this._requestDraw();
|
||||
// chainable
|
||||
return this;
|
||||
}
|
||||
destroy() {
|
||||
if (this.hasChildren()) {
|
||||
this.destroyChildren();
|
||||
}
|
||||
super.destroy();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* return an array of nodes that match the selector.
|
||||
* You can provide a string with '#' for id selections and '.' for name selections.
|
||||
* Or a function that will return true/false when a node is passed through. See example below.
|
||||
* With strings you can also select by type or class name. Pass multiple selectors
|
||||
* separated by a comma.
|
||||
* @method
|
||||
* @name Konva.Container#find
|
||||
* @param {String | Function} selector
|
||||
* @returns {Array}
|
||||
* @example
|
||||
*
|
||||
* Passing a string as a selector
|
||||
* // select node with id foo
|
||||
* var node = stage.find('#foo');
|
||||
*
|
||||
* // select nodes with name bar inside layer
|
||||
* var nodes = layer.find('.bar');
|
||||
*
|
||||
* // select all groups inside layer
|
||||
* var nodes = layer.find('Group');
|
||||
*
|
||||
* // select all rectangles inside layer
|
||||
* var nodes = layer.find('Rect');
|
||||
*
|
||||
* // select node with an id of foo or a name of bar inside layer
|
||||
* var nodes = layer.find('#foo, .bar');
|
||||
*
|
||||
* Passing a function as a selector
|
||||
*
|
||||
* // get all groups with a function
|
||||
* var groups = stage.find(node => {
|
||||
* return node.getType() === 'Group';
|
||||
* });
|
||||
*
|
||||
* // get only Nodes with partial opacity
|
||||
* var alphaNodes = layer.find(node => {
|
||||
* return node.getType() === 'Node' && node.getAbsoluteOpacity() < 1;
|
||||
* });
|
||||
*/
|
||||
find(selector) {
|
||||
// protecting _generalFind to prevent user from accidentally adding
|
||||
// second argument and getting unexpected `findOne` result
|
||||
return this._generalFind(selector, false);
|
||||
}
|
||||
/**
|
||||
* return a first node from `find` method
|
||||
* @method
|
||||
* @name Konva.Container#findOne
|
||||
* @param {String | Function} selector
|
||||
* @returns {Konva.Node | Undefined}
|
||||
* @example
|
||||
* // select node with id foo
|
||||
* var node = stage.findOne('#foo');
|
||||
*
|
||||
* // select node with name bar inside layer
|
||||
* var nodes = layer.findOne('.bar');
|
||||
*
|
||||
* // select the first node to return true in a function
|
||||
* var node = stage.findOne(node => {
|
||||
* return node.getType() === 'Shape'
|
||||
* })
|
||||
*/
|
||||
findOne(selector) {
|
||||
const result = this._generalFind(selector, true);
|
||||
return result.length > 0 ? result[0] : undefined;
|
||||
}
|
||||
_generalFind(selector, findOne) {
|
||||
const retArr = [];
|
||||
this._descendants((node) => {
|
||||
const valid = node._isMatch(selector);
|
||||
if (valid) {
|
||||
retArr.push(node);
|
||||
}
|
||||
if (valid && findOne) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return retArr;
|
||||
}
|
||||
_descendants(fn) {
|
||||
let shouldStop = false;
|
||||
for (const child of this.children) {
|
||||
shouldStop = fn(child);
|
||||
if (shouldStop) {
|
||||
return true;
|
||||
}
|
||||
if (!child.hasChildren()) {
|
||||
continue;
|
||||
}
|
||||
shouldStop = child._descendants(fn);
|
||||
if (shouldStop) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// extenders
|
||||
toObject() {
|
||||
const obj = Node.prototype.toObject.call(this);
|
||||
obj.children = [];
|
||||
this.children.forEach((child) => {
|
||||
obj.children.push(child.toObject());
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
/**
|
||||
* determine if node is an ancestor
|
||||
* of descendant
|
||||
* @method
|
||||
* @name Konva.Container#isAncestorOf
|
||||
* @param {Konva.Node} node
|
||||
*/
|
||||
isAncestorOf(node) {
|
||||
let parent = node.getParent();
|
||||
while (parent) {
|
||||
if (parent._id === this._id) {
|
||||
return true;
|
||||
}
|
||||
parent = parent.getParent();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
clone(obj) {
|
||||
// call super method
|
||||
const node = Node.prototype.clone.call(this, obj);
|
||||
this.children.forEach(function (no) {
|
||||
node.add(no.clone());
|
||||
});
|
||||
return node;
|
||||
}
|
||||
/**
|
||||
* get all shapes that intersect a point. Note: because this method must clear a temporary
|
||||
* canvas and redraw every shape inside the container, it should only be used for special situations
|
||||
* because it performs very poorly. Please use the {@link Konva.Stage#getIntersection} method if at all possible
|
||||
* because it performs much better.
|
||||
* Nodes with listening set to false or invisible nodes are not detected
|
||||
* @method
|
||||
* @name Konva.Container#getAllIntersections
|
||||
* @param {Object} pos
|
||||
* @param {Number} pos.x
|
||||
* @param {Number} pos.y
|
||||
* @returns {Array} array of shapes
|
||||
*/
|
||||
getAllIntersections(pos) {
|
||||
const arr = [];
|
||||
this.find('Shape').forEach((shape) => {
|
||||
if (shape.isVisible() && shape.intersects(pos)) {
|
||||
arr.push(shape);
|
||||
}
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
_clearSelfAndDescendantCache(attr) {
|
||||
var _a;
|
||||
// Perf: an absoluteTransform cascade is an update batch, so a listener on
|
||||
// one of N descendants defers its work until the outermost walk returns.
|
||||
// try/finally so depth/flush survive listener exceptions.
|
||||
const isAbsTransform = attr === undefined || attr === 'absoluteTransform';
|
||||
if (isAbsTransform)
|
||||
Node._updateBatchDepth++;
|
||||
try {
|
||||
super._clearSelfAndDescendantCache(attr);
|
||||
// a cached container is drawn from its bitmap, so its descendants keep
|
||||
// their transform caches while it moves (perf); Node.getAbsoluteTransform
|
||||
// skips those caches under a cached ancestor
|
||||
if (attr === 'absoluteTransform' && this.isCached())
|
||||
return;
|
||||
(_a = this.children) === null || _a === void 0 ? void 0 : _a.forEach(function (node) {
|
||||
node._clearSelfAndDescendantCache(attr);
|
||||
});
|
||||
}
|
||||
finally {
|
||||
if (isAbsTransform && --Node._updateBatchDepth === 0) {
|
||||
Node._flushUpdateBatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
_setChildrenIndices() {
|
||||
var _a;
|
||||
(_a = this.children) === null || _a === void 0 ? void 0 : _a.forEach(function (child, n) {
|
||||
child.index = n;
|
||||
});
|
||||
this._notifySubtreeChange();
|
||||
this._requestDraw();
|
||||
}
|
||||
drawScene(can, top) {
|
||||
const layer = this.getLayer(), canvas = can || (layer && layer.getCanvas()), context = canvas && canvas.getContext(), cachedCanvas = this._getCanvasCache(), cachedSceneCanvas = cachedCanvas && cachedCanvas.scene;
|
||||
const caching = top === this;
|
||||
if (!(top ? this._isVisible(top) : this.isVisible()) && !caching) {
|
||||
return this;
|
||||
}
|
||||
if (cachedSceneCanvas) {
|
||||
context.save();
|
||||
const m = this.getAbsoluteTransform(top).getMatrix();
|
||||
context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
this._drawCachedSceneCanvas(context);
|
||||
context.restore();
|
||||
}
|
||||
else {
|
||||
this._drawChildren('drawScene', canvas, top);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
drawHit(can, top) {
|
||||
if (!this.shouldDrawHit(top)) {
|
||||
return this;
|
||||
}
|
||||
const layer = this.getLayer(), canvas = can || (layer && layer.hitCanvas), context = canvas && canvas.getContext(), cachedHitCanvas = this._getCachedHitCanvas(top);
|
||||
if (cachedHitCanvas) {
|
||||
context.save();
|
||||
const m = this.getAbsoluteTransform(top).getMatrix();
|
||||
context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
this._drawCachedHitCanvas(context, cachedHitCanvas);
|
||||
context.restore();
|
||||
}
|
||||
else {
|
||||
this._drawChildren('drawHit', canvas, top);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
_drawChildren(drawMethod, canvas, top) {
|
||||
const context = canvas && canvas.getContext(), clipWidth = this.clipWidth(), clipHeight = this.clipHeight(), clipFunc = this.clipFunc(), hasClip = (typeof clipWidth === 'number' && typeof clipHeight === 'number') ||
|
||||
clipFunc;
|
||||
const selfCache = top === this;
|
||||
if (hasClip) {
|
||||
context.save();
|
||||
const transform = this.getAbsoluteTransform(top);
|
||||
let m = transform.getMatrix();
|
||||
context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
context.beginPath();
|
||||
let clipArgs;
|
||||
if (clipFunc) {
|
||||
clipArgs = clipFunc.call(this, context, this);
|
||||
}
|
||||
else {
|
||||
const clipX = this.clipX();
|
||||
const clipY = this.clipY();
|
||||
context.rect(clipX || 0, clipY || 0, clipWidth, clipHeight);
|
||||
}
|
||||
context.clip.apply(context, clipArgs);
|
||||
m = transform.copy().invert().getMatrix();
|
||||
context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
}
|
||||
const hasComposition = !selfCache &&
|
||||
this.globalCompositeOperation() !== 'source-over' &&
|
||||
drawMethod === 'drawScene';
|
||||
if (hasComposition) {
|
||||
context.save();
|
||||
context._applyGlobalCompositeOperation(this);
|
||||
}
|
||||
try {
|
||||
this._drawChildNodes(drawMethod, canvas, top);
|
||||
}
|
||||
finally {
|
||||
if (hasComposition) {
|
||||
context.restore();
|
||||
}
|
||||
if (hasClip) {
|
||||
context.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
_drawChildNodes(drawMethod, canvas, top) {
|
||||
var _a;
|
||||
(_a = this.children) === null || _a === void 0 ? void 0 : _a.forEach(function (child) {
|
||||
child[drawMethod](canvas, top);
|
||||
});
|
||||
}
|
||||
getClientRect(config = {}) {
|
||||
var _a;
|
||||
const cachedRect = this._getCachedSceneRect(config);
|
||||
if (cachedRect)
|
||||
return cachedRect;
|
||||
const skipTransform = config.skipTransform;
|
||||
const relativeTo = config.relativeTo;
|
||||
const [a, b, c, d] = this.getAbsoluteTransform().getMatrix();
|
||||
// A zero scale cannot project local padding for a non-scaling stroke.
|
||||
const measureTransformed = !skipTransform && (config._forDrawing || a * d - b * c === 0);
|
||||
let minX, minY, maxX, maxY;
|
||||
let selfRect = {
|
||||
x: Infinity,
|
||||
y: Infinity,
|
||||
width: 0,
|
||||
height: 0,
|
||||
};
|
||||
const that = this;
|
||||
(_a = this.children) === null || _a === void 0 ? void 0 : _a.forEach(function (child) {
|
||||
// skip invisible children
|
||||
if (!child.visible()) {
|
||||
return;
|
||||
}
|
||||
const rect = child.getClientRect({
|
||||
relativeTo: measureTransformed ? relativeTo : that,
|
||||
_forDrawing: config._forDrawing,
|
||||
skipShadow: config.skipShadow,
|
||||
skipStroke: config.skipStroke,
|
||||
});
|
||||
// skip invisible children (like empty groups) and a child whose rect
|
||||
// would poison the whole box
|
||||
if ((rect.width === 0 && rect.height === 0) ||
|
||||
!isFinite(rect.x + rect.y + rect.width + rect.height)) {
|
||||
return;
|
||||
}
|
||||
if (minX === undefined) {
|
||||
// initial value for first child
|
||||
minX = rect.x;
|
||||
minY = rect.y;
|
||||
maxX = rect.x + rect.width;
|
||||
maxY = rect.y + rect.height;
|
||||
}
|
||||
else {
|
||||
minX = Math.min(minX, rect.x);
|
||||
minY = Math.min(minY, rect.y);
|
||||
maxX = Math.max(maxX, rect.x + rect.width);
|
||||
maxY = Math.max(maxY, rect.y + rect.height);
|
||||
}
|
||||
});
|
||||
if (minX !== undefined) {
|
||||
selfRect = {
|
||||
x: minX,
|
||||
y: minY,
|
||||
width: maxX - minX,
|
||||
height: maxY - minY,
|
||||
};
|
||||
}
|
||||
else {
|
||||
selfRect = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
};
|
||||
}
|
||||
if (!skipTransform && !measureTransformed) {
|
||||
return this._transformedRect(selfRect, relativeTo);
|
||||
}
|
||||
return selfRect;
|
||||
}
|
||||
}
|
||||
// add getters setters
|
||||
Factory.addComponentsGetterSetter(Container, 'clip', [
|
||||
'x',
|
||||
'y',
|
||||
'width',
|
||||
'height',
|
||||
]);
|
||||
/**
|
||||
* get/set clip
|
||||
* @method
|
||||
* @name Konva.Container#clip
|
||||
* @param {Object} clip
|
||||
* @param {Number} clip.x
|
||||
* @param {Number} clip.y
|
||||
* @param {Number} clip.width
|
||||
* @param {Number} clip.height
|
||||
* @returns {Object}
|
||||
* @example
|
||||
* // get clip
|
||||
* var clip = container.clip();
|
||||
*
|
||||
* // set clip
|
||||
* container.clip({
|
||||
* x: 20,
|
||||
* y: 20,
|
||||
* width: 20,
|
||||
* height: 20
|
||||
* });
|
||||
*/
|
||||
Factory.addGetterSetter(Container, 'clipX', undefined, getNumberValidator());
|
||||
/**
|
||||
* get/set clip x
|
||||
* @name Konva.Container#clipX
|
||||
* @method
|
||||
* @param {Number} x
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* // get clip x
|
||||
* var clipX = container.clipX();
|
||||
*
|
||||
* // set clip x
|
||||
* container.clipX(10);
|
||||
*/
|
||||
Factory.addGetterSetter(Container, 'clipY', undefined, getNumberValidator());
|
||||
/**
|
||||
* get/set clip y
|
||||
* @name Konva.Container#clipY
|
||||
* @method
|
||||
* @param {Number} y
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* // get clip y
|
||||
* var clipY = container.clipY();
|
||||
*
|
||||
* // set clip y
|
||||
* container.clipY(10);
|
||||
*/
|
||||
Factory.addGetterSetter(Container, 'clipWidth', undefined, getNumberValidator());
|
||||
/**
|
||||
* get/set clip width
|
||||
* @name Konva.Container#clipWidth
|
||||
* @method
|
||||
* @param {Number} width
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* // get clip width
|
||||
* var clipWidth = container.clipWidth();
|
||||
*
|
||||
* // set clip width
|
||||
* container.clipWidth(100);
|
||||
*/
|
||||
Factory.addGetterSetter(Container, 'clipHeight', undefined, getNumberValidator());
|
||||
/**
|
||||
* get/set clip height
|
||||
* @name Konva.Container#clipHeight
|
||||
* @method
|
||||
* @param {Number} height
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* // get clip height
|
||||
* var clipHeight = container.clipHeight();
|
||||
*
|
||||
* // set clip height
|
||||
* container.clipHeight(100);
|
||||
*/
|
||||
Factory.addGetterSetter(Container, 'clipFunc');
|
||||
/**
|
||||
* get/set clip function
|
||||
* @name Konva.Container#clipFunc
|
||||
* @method
|
||||
* @param {Function} function
|
||||
* @returns {Function}
|
||||
* @example
|
||||
* // get clip function
|
||||
* var clipFunction = container.clipFunc();
|
||||
*
|
||||
* // set clip function
|
||||
* container.clipFunc(function(ctx) {
|
||||
* ctx.rect(0, 0, 100, 100);
|
||||
* });
|
||||
*
|
||||
* container.clipFunc(function(ctx) {
|
||||
* // optionally return a clip Path2D and clip-rule or just the clip-rule
|
||||
* return [new Path2D('M0 0v50h50Z'), 'evenodd']
|
||||
* });
|
||||
*/
|
||||
+344
@@ -0,0 +1,344 @@
|
||||
import type { Canvas, SceneCanvas } from './Canvas.ts';
|
||||
import type { Shape } from './Shape.ts';
|
||||
import type { IRect } from './types.ts';
|
||||
import type { Node } from './Node.ts';
|
||||
declare const CONTEXT_PROPERTIES: readonly ["fillStyle", "strokeStyle", "shadowColor", "shadowBlur", "shadowOffsetX", "shadowOffsetY", "letterSpacing", "lineCap", "lineDashOffset", "lineJoin", "lineWidth", "miterLimit", "direction", "font", "textAlign", "textBaseline", "globalAlpha", "globalCompositeOperation", "imageSmoothingEnabled", "imageSmoothingQuality", "filter"];
|
||||
export declare function isCSSFiltersSupported(): boolean;
|
||||
interface ExtendedCanvasRenderingContext2D extends CanvasRenderingContext2D {
|
||||
letterSpacing: string;
|
||||
}
|
||||
/**
|
||||
* Konva wrapper around native 2d canvas context. It has almost the same API of 2d context with some additional functions.
|
||||
* With core Konva shapes you don't need to use this object. But you will use it if you want to create
|
||||
* a [custom shape](/docs/react/Custom_Shape.html) or a [custom hit regions](/docs/events/Custom_Hit_Region.html).
|
||||
* For full information about each 2d context API use [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D)
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* const rect = new Konva.Shape({
|
||||
* fill: 'red',
|
||||
* width: 100,
|
||||
* height: 100,
|
||||
* sceneFunc: (ctx, shape) => {
|
||||
* // ctx - is context wrapper
|
||||
* // shape - is instance of Konva.Shape, so it equals to "rect" variable
|
||||
* ctx.rect(0, 0, shape.getAttr('width'), shape.getAttr('height'));
|
||||
*
|
||||
* // automatically fill shape from props and draw hit region
|
||||
* ctx.fillStrokeShape(shape);
|
||||
* }
|
||||
* })
|
||||
*/
|
||||
export declare class Context {
|
||||
canvas: Canvas;
|
||||
_context: CanvasRenderingContext2D;
|
||||
traceArr: Array<string>;
|
||||
constructor(canvas: Canvas);
|
||||
/**
|
||||
* fill shape
|
||||
* @method
|
||||
* @name Konva.Context#fillShape
|
||||
* @param {Konva.Shape} shape
|
||||
*/
|
||||
fillShape(shape: Shape): void;
|
||||
_fill(shape: Shape): void;
|
||||
/**
|
||||
* stroke shape
|
||||
* @method
|
||||
* @name Konva.Context#strokeShape
|
||||
* @param {Konva.Shape} shape
|
||||
*/
|
||||
strokeShape(shape: Shape): void;
|
||||
_stroke(shape: Shape): void;
|
||||
/**
|
||||
* fill then stroke
|
||||
* @method
|
||||
* @name Konva.Context#fillStrokeShape
|
||||
* @param {Konva.Shape} shape
|
||||
*/
|
||||
fillStrokeShape(shape: Shape): void;
|
||||
getTrace(relaxed?: boolean, rounded?: boolean): string;
|
||||
clearTrace(): void;
|
||||
_trace(str: any): void;
|
||||
/**
|
||||
* reset canvas context transform
|
||||
* @method
|
||||
* @name Konva.Context#reset
|
||||
*/
|
||||
reset(): void;
|
||||
/**
|
||||
* get canvas wrapper
|
||||
* @method
|
||||
* @name Konva.Context#getCanvas
|
||||
* @returns {Konva.Canvas}
|
||||
*/
|
||||
getCanvas(): Canvas;
|
||||
/**
|
||||
* clear canvas
|
||||
* @method
|
||||
* @name Konva.Context#clear
|
||||
* @param {Object} [bounds]
|
||||
* @param {Number} [bounds.x]
|
||||
* @param {Number} [bounds.y]
|
||||
* @param {Number} [bounds.width]
|
||||
* @param {Number} [bounds.height]
|
||||
*/
|
||||
clear(bounds?: IRect): void;
|
||||
_drawDeviceBuffer(canvas: SceneCanvas): void;
|
||||
_applyLineCap(shape: Shape): void;
|
||||
_opacityRoot: Node | undefined;
|
||||
_getOpacity(shape: Node): any;
|
||||
_applyOpacity(shape: Node): void;
|
||||
_applyLineJoin(shape: Shape): void;
|
||||
_applyMiterLimit(shape: Shape): void;
|
||||
setAttr(attr: string, val: any): void;
|
||||
/**
|
||||
* arc function.
|
||||
* @method
|
||||
* @name Konva.Context#arc
|
||||
*/
|
||||
arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterClockwise?: boolean): void;
|
||||
/**
|
||||
* arcTo function.
|
||||
* @method
|
||||
* @name Konva.Context#arcTo
|
||||
*
|
||||
*/
|
||||
arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void;
|
||||
/**
|
||||
* beginPath function.
|
||||
* @method
|
||||
* @name Konva.Context#beginPath
|
||||
*/
|
||||
beginPath(): void;
|
||||
/**
|
||||
* bezierCurveTo function.
|
||||
* @method
|
||||
* @name Konva.Context#bezierCurveTo
|
||||
*/
|
||||
bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void;
|
||||
/**
|
||||
* clearRect function.
|
||||
* @method
|
||||
* @name Konva.Context#clearRect
|
||||
*/
|
||||
clearRect(x: number, y: number, width: number, height: number): void;
|
||||
/**
|
||||
* clip function.
|
||||
* @method
|
||||
* @name Konva.Context#clip
|
||||
*/
|
||||
clip(fillRule?: CanvasFillRule): void;
|
||||
clip(path: Path2D, fillRule?: CanvasFillRule): void;
|
||||
/**
|
||||
* closePath function.
|
||||
* @method
|
||||
* @name Konva.Context#closePath
|
||||
*/
|
||||
closePath(): void;
|
||||
/**
|
||||
* createImageData function.
|
||||
* @method
|
||||
* @name Konva.Context#createImageData
|
||||
*/
|
||||
createImageData(width: any, height: any): ImageData | undefined;
|
||||
/**
|
||||
* createLinearGradient function.
|
||||
* @method
|
||||
* @name Konva.Context#createLinearGradient
|
||||
*/
|
||||
createLinearGradient(x0: number, y0: number, x1: number, y1: number): CanvasGradient;
|
||||
/**
|
||||
* createPattern function.
|
||||
* @method
|
||||
* @name Konva.Context#createPattern
|
||||
*/
|
||||
createPattern(image: CanvasImageSource, repetition: string | null): CanvasPattern | null;
|
||||
/**
|
||||
* createRadialGradient function.
|
||||
* @method
|
||||
* @name Konva.Context#createRadialGradient
|
||||
*/
|
||||
createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): CanvasGradient;
|
||||
/**
|
||||
* drawImage function.
|
||||
* @method
|
||||
* @name Konva.Context#drawImage
|
||||
*/
|
||||
drawImage(image: CanvasImageSource, sx: number, sy: number, sWidth?: number, sHeight?: number, dx?: number, dy?: number, dWidth?: number, dHeight?: number): void;
|
||||
/**
|
||||
* ellipse function.
|
||||
* @method
|
||||
* @name Konva.Context#ellipse
|
||||
*/
|
||||
ellipse(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void;
|
||||
/**
|
||||
* isPointInPath function.
|
||||
* @method
|
||||
* @name Konva.Context#isPointInPath
|
||||
*/
|
||||
isPointInPath(x: number, y: number, path?: Path2D, fillRule?: CanvasFillRule): boolean;
|
||||
/**
|
||||
* fill function.
|
||||
* @method
|
||||
* @name Konva.Context#fill
|
||||
*/
|
||||
fill(fillRule?: CanvasFillRule): void;
|
||||
fill(path: Path2D, fillRule?: CanvasFillRule): void;
|
||||
/**
|
||||
* fillRect function.
|
||||
* @method
|
||||
* @name Konva.Context#fillRect
|
||||
*/
|
||||
fillRect(x: number, y: number, width: number, height: number): void;
|
||||
/**
|
||||
* strokeRect function.
|
||||
* @method
|
||||
* @name Konva.Context#strokeRect
|
||||
*/
|
||||
strokeRect(x: number, y: number, width: number, height: number): void;
|
||||
/**
|
||||
* fillText function.
|
||||
* @method
|
||||
* @name Konva.Context#fillText
|
||||
*/
|
||||
fillText(text: string, x: number, y: number, maxWidth?: number): void;
|
||||
/**
|
||||
* measureText function.
|
||||
* @method
|
||||
* @name Konva.Context#measureText
|
||||
*/
|
||||
measureText(text: string): TextMetrics;
|
||||
/**
|
||||
* getImageData function.
|
||||
* @method
|
||||
* @name Konva.Context#getImageData
|
||||
*/
|
||||
getImageData(sx: number, sy: number, sw: number, sh: number): ImageData;
|
||||
/**
|
||||
* lineTo function.
|
||||
* @method
|
||||
* @name Konva.Context#lineTo
|
||||
*/
|
||||
lineTo(x: number, y: number): void;
|
||||
/**
|
||||
* moveTo function.
|
||||
* @method
|
||||
* @name Konva.Context#moveTo
|
||||
*/
|
||||
moveTo(x: number, y: number): void;
|
||||
/**
|
||||
* rect function.
|
||||
* @method
|
||||
* @name Konva.Context#rect
|
||||
*/
|
||||
rect(x: number, y: number, width: number, height: number): void;
|
||||
/**
|
||||
* roundRect function.
|
||||
* @method
|
||||
* @name Konva.Context#roundRect
|
||||
*/
|
||||
roundRect(x: number, y: number, width: number, height: number, radii: number | DOMPointInit | (number | DOMPointInit)[]): void;
|
||||
/**
|
||||
* putImageData function.
|
||||
* @method
|
||||
* @name Konva.Context#putImageData
|
||||
*/
|
||||
putImageData(imageData: ImageData, dx: number, dy: number): void;
|
||||
/**
|
||||
* quadraticCurveTo function.
|
||||
* @method
|
||||
* @name Konva.Context#quadraticCurveTo
|
||||
*/
|
||||
quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void;
|
||||
/**
|
||||
* restore function.
|
||||
* @method
|
||||
* @name Konva.Context#restore
|
||||
*/
|
||||
restore(): void;
|
||||
/**
|
||||
* rotate function.
|
||||
* @method
|
||||
* @name Konva.Context#rotate
|
||||
*/
|
||||
rotate(angle: number): void;
|
||||
/**
|
||||
* save function.
|
||||
* @method
|
||||
* @name Konva.Context#save
|
||||
*/
|
||||
save(): void;
|
||||
/**
|
||||
* scale function.
|
||||
* @method
|
||||
* @name Konva.Context#scale
|
||||
*/
|
||||
scale(x: number, y: number): void;
|
||||
/**
|
||||
* setLineDash function.
|
||||
* @method
|
||||
* @name Konva.Context#setLineDash
|
||||
*/
|
||||
setLineDash(segments: number[]): void;
|
||||
/**
|
||||
* getLineDash function.
|
||||
* @method
|
||||
* @name Konva.Context#getLineDash
|
||||
*/
|
||||
getLineDash(): number[];
|
||||
/**
|
||||
* setTransform function.
|
||||
* @method
|
||||
* @name Konva.Context#setTransform
|
||||
*/
|
||||
setTransform(a: number, b: number, c: number, d: number, e: number, f: number): void;
|
||||
/**
|
||||
* stroke function.
|
||||
* @method
|
||||
* @name Konva.Context#stroke
|
||||
*/
|
||||
stroke(path2d?: Path2D): void;
|
||||
/**
|
||||
* strokeText function.
|
||||
* @method
|
||||
* @name Konva.Context#strokeText
|
||||
*/
|
||||
strokeText(text: string, x: number, y: number, maxWidth?: number): void;
|
||||
/**
|
||||
* transform function.
|
||||
* @method
|
||||
* @name Konva.Context#transform
|
||||
*/
|
||||
transform(a: number, b: number, c: number, d: number, e: number, f: number): void;
|
||||
/**
|
||||
* translate function.
|
||||
* @method
|
||||
* @name Konva.Context#translate
|
||||
*/
|
||||
translate(x: number, y: number): void;
|
||||
_enableTrace(): void;
|
||||
_applyGlobalCompositeOperation(node: any): void;
|
||||
}
|
||||
type CanvasContextProps = Pick<ExtendedCanvasRenderingContext2D, (typeof CONTEXT_PROPERTIES)[number]>;
|
||||
export interface Context extends CanvasContextProps {
|
||||
}
|
||||
export declare class SceneContext extends Context {
|
||||
constructor(canvas: Canvas, { willReadFrequently }?: {
|
||||
willReadFrequently?: boolean | undefined;
|
||||
});
|
||||
_getFillPattern(shape: Shape): any;
|
||||
_getFillStyle(shape: Shape): any;
|
||||
_fill(shape: Shape): void;
|
||||
_strokeLinearGradient(shape: any): void;
|
||||
_applyStrokeStyle(shape: Shape): void;
|
||||
_stroke(shape: any): void;
|
||||
_applyShadow(shape: any): void;
|
||||
}
|
||||
export declare class HitContext extends Context {
|
||||
constructor(canvas: Canvas);
|
||||
_fill(shape: Shape): void;
|
||||
strokeShape(shape: Shape): void;
|
||||
_stroke(shape: any): void;
|
||||
}
|
||||
export {};
|
||||
+831
@@ -0,0 +1,831 @@
|
||||
import { Transform, Util } from "./Util.js";
|
||||
import { Konva } from "./Global.js";
|
||||
function simplifyArray(arr) {
|
||||
const retArr = [], len = arr.length, util = Util;
|
||||
for (let n = 0; n < len; n++) {
|
||||
let val = arr[n];
|
||||
if (util._isNumber(val)) {
|
||||
val = Math.round(val * 1000) / 1000;
|
||||
}
|
||||
else if (!util._isString(val)) {
|
||||
val = val + '';
|
||||
}
|
||||
retArr.push(val);
|
||||
}
|
||||
return retArr;
|
||||
}
|
||||
const COMMA = ',', OPEN_PAREN = '(', CLOSE_PAREN = ')', SEMICOLON = ';', DOUBLE_PAREN = '()',
|
||||
// EMPTY_STRING = '',
|
||||
EQUALS = '=',
|
||||
// SET = 'set',
|
||||
CONTEXT_METHODS = [
|
||||
'arc',
|
||||
'arcTo',
|
||||
'beginPath',
|
||||
'bezierCurveTo',
|
||||
'clearRect',
|
||||
'clip',
|
||||
'closePath',
|
||||
'createLinearGradient',
|
||||
'createPattern',
|
||||
'createRadialGradient',
|
||||
'drawImage',
|
||||
'ellipse',
|
||||
'fill',
|
||||
'fillText',
|
||||
'getImageData',
|
||||
'createImageData',
|
||||
'lineTo',
|
||||
'moveTo',
|
||||
'putImageData',
|
||||
'quadraticCurveTo',
|
||||
'rect',
|
||||
'roundRect',
|
||||
'restore',
|
||||
'rotate',
|
||||
'save',
|
||||
'scale',
|
||||
'setLineDash',
|
||||
'setTransform',
|
||||
'stroke',
|
||||
'strokeText',
|
||||
'transform',
|
||||
'translate',
|
||||
];
|
||||
const CONTEXT_PROPERTIES = [
|
||||
'fillStyle',
|
||||
'strokeStyle',
|
||||
'shadowColor',
|
||||
'shadowBlur',
|
||||
'shadowOffsetX',
|
||||
'shadowOffsetY',
|
||||
'letterSpacing',
|
||||
'lineCap',
|
||||
'lineDashOffset',
|
||||
'lineJoin',
|
||||
'lineWidth',
|
||||
'miterLimit',
|
||||
'direction',
|
||||
'font',
|
||||
'textAlign',
|
||||
'textBaseline',
|
||||
'globalAlpha',
|
||||
'globalCompositeOperation',
|
||||
'imageSmoothingEnabled',
|
||||
'imageSmoothingQuality',
|
||||
'filter',
|
||||
];
|
||||
const traceArrMax = 100;
|
||||
// Check if CSS filters are supported in the current browser
|
||||
let _cssFiltersSupported = null;
|
||||
export function isCSSFiltersSupported() {
|
||||
if (_cssFiltersSupported !== null) {
|
||||
return _cssFiltersSupported;
|
||||
}
|
||||
try {
|
||||
const canvas = Util.createCanvasElement();
|
||||
const ctx = canvas.getContext('2d');
|
||||
_cssFiltersSupported = !!ctx && 'filter' in ctx;
|
||||
Util.releaseCanvas(canvas);
|
||||
}
|
||||
catch (e) {
|
||||
_cssFiltersSupported = false;
|
||||
}
|
||||
return _cssFiltersSupported;
|
||||
}
|
||||
/**
|
||||
* Konva wrapper around native 2d canvas context. It has almost the same API of 2d context with some additional functions.
|
||||
* With core Konva shapes you don't need to use this object. But you will use it if you want to create
|
||||
* a [custom shape](/docs/react/Custom_Shape.html) or a [custom hit regions](/docs/events/Custom_Hit_Region.html).
|
||||
* For full information about each 2d context API use [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D)
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* const rect = new Konva.Shape({
|
||||
* fill: 'red',
|
||||
* width: 100,
|
||||
* height: 100,
|
||||
* sceneFunc: (ctx, shape) => {
|
||||
* // ctx - is context wrapper
|
||||
* // shape - is instance of Konva.Shape, so it equals to "rect" variable
|
||||
* ctx.rect(0, 0, shape.getAttr('width'), shape.getAttr('height'));
|
||||
*
|
||||
* // automatically fill shape from props and draw hit region
|
||||
* ctx.fillStrokeShape(shape);
|
||||
* }
|
||||
* })
|
||||
*/
|
||||
export class Context {
|
||||
constructor(canvas) {
|
||||
this.canvas = canvas;
|
||||
if (Konva.enableTrace) {
|
||||
this.traceArr = [];
|
||||
this._enableTrace();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* fill shape
|
||||
* @method
|
||||
* @name Konva.Context#fillShape
|
||||
* @param {Konva.Shape} shape
|
||||
*/
|
||||
fillShape(shape) {
|
||||
if (shape.fillEnabled()) {
|
||||
this._fill(shape);
|
||||
}
|
||||
}
|
||||
_fill(shape) {
|
||||
// abstract
|
||||
}
|
||||
/**
|
||||
* stroke shape
|
||||
* @method
|
||||
* @name Konva.Context#strokeShape
|
||||
* @param {Konva.Shape} shape
|
||||
*/
|
||||
strokeShape(shape) {
|
||||
if (shape.hasStroke()) {
|
||||
this._stroke(shape);
|
||||
}
|
||||
}
|
||||
_stroke(shape) {
|
||||
// abstract
|
||||
}
|
||||
/**
|
||||
* fill then stroke
|
||||
* @method
|
||||
* @name Konva.Context#fillStrokeShape
|
||||
* @param {Konva.Shape} shape
|
||||
*/
|
||||
fillStrokeShape(shape) {
|
||||
if (shape.attrs.fillAfterStrokeEnabled) {
|
||||
this.strokeShape(shape);
|
||||
this.fillShape(shape);
|
||||
}
|
||||
else {
|
||||
this.fillShape(shape);
|
||||
this.strokeShape(shape);
|
||||
}
|
||||
}
|
||||
getTrace(relaxed, rounded) {
|
||||
let traceArr = this.traceArr, len = traceArr.length, str = '', n, trace, method, args;
|
||||
for (n = 0; n < len; n++) {
|
||||
trace = traceArr[n];
|
||||
method = trace.method;
|
||||
// methods
|
||||
if (method) {
|
||||
args = trace.args;
|
||||
str += method;
|
||||
if (relaxed) {
|
||||
str += DOUBLE_PAREN;
|
||||
}
|
||||
else {
|
||||
if (rounded) {
|
||||
args = args.map((a) => (typeof a === 'number' ? Math.floor(a) : a));
|
||||
}
|
||||
str += OPEN_PAREN + args.join(COMMA) + CLOSE_PAREN;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// properties
|
||||
str += trace.property;
|
||||
if (!relaxed) {
|
||||
str += EQUALS + trace.val;
|
||||
}
|
||||
}
|
||||
str += SEMICOLON;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
clearTrace() {
|
||||
this.traceArr = [];
|
||||
}
|
||||
_trace(str) {
|
||||
let traceArr = this.traceArr, len;
|
||||
traceArr.push(str);
|
||||
len = traceArr.length;
|
||||
if (len >= traceArrMax) {
|
||||
traceArr.shift();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* reset canvas context transform
|
||||
* @method
|
||||
* @name Konva.Context#reset
|
||||
*/
|
||||
reset() {
|
||||
const pixelRatio = this.getCanvas().getPixelRatio();
|
||||
this.setTransform(1 * pixelRatio, 0, 0, 1 * pixelRatio, 0, 0);
|
||||
}
|
||||
/**
|
||||
* get canvas wrapper
|
||||
* @method
|
||||
* @name Konva.Context#getCanvas
|
||||
* @returns {Konva.Canvas}
|
||||
*/
|
||||
getCanvas() {
|
||||
return this.canvas;
|
||||
}
|
||||
/**
|
||||
* clear canvas
|
||||
* @method
|
||||
* @name Konva.Context#clear
|
||||
* @param {Object} [bounds]
|
||||
* @param {Number} [bounds.x]
|
||||
* @param {Number} [bounds.y]
|
||||
* @param {Number} [bounds.width]
|
||||
* @param {Number} [bounds.height]
|
||||
*/
|
||||
clear(bounds) {
|
||||
const canvas = this.getCanvas();
|
||||
if (bounds) {
|
||||
this.clearRect(bounds.x || 0, bounds.y || 0, bounds.width || 0, bounds.height || 0);
|
||||
}
|
||||
else {
|
||||
this.clearRect(0, 0, canvas.getWidth() / canvas.pixelRatio, canvas.getHeight() / canvas.pixelRatio);
|
||||
}
|
||||
}
|
||||
// Copy a buffer already rasterized in destination pixels. The caller owns
|
||||
// save/restore, opacity, compositing and the destination clip.
|
||||
_drawDeviceBuffer(canvas) {
|
||||
const { x, y, width, height } = canvas._isolationRect;
|
||||
this.setTransform(1, 0, 0, 1, 0, 0);
|
||||
this.imageSmoothingEnabled = false;
|
||||
this.drawImage(canvas._canvas, 0, 0, width, height, x, y, width, height);
|
||||
}
|
||||
_applyLineCap(shape) {
|
||||
const lineCap = shape.attrs.lineCap;
|
||||
if (lineCap) {
|
||||
this.setAttr('lineCap', lineCap);
|
||||
}
|
||||
}
|
||||
_getOpacity(shape) {
|
||||
if (!this._opacityRoot) {
|
||||
return shape.getAbsoluteOpacity();
|
||||
}
|
||||
let opacity = shape.opacity();
|
||||
let parent = shape.parent;
|
||||
while (parent && parent !== this._opacityRoot) {
|
||||
opacity *= parent.opacity();
|
||||
parent = parent.parent;
|
||||
}
|
||||
return opacity;
|
||||
}
|
||||
_applyOpacity(shape) {
|
||||
const absOpacity = this._getOpacity(shape);
|
||||
if (absOpacity !== 1) {
|
||||
this.setAttr('globalAlpha', absOpacity);
|
||||
}
|
||||
}
|
||||
_applyLineJoin(shape) {
|
||||
const lineJoin = shape.attrs.lineJoin;
|
||||
if (lineJoin) {
|
||||
this.setAttr('lineJoin', lineJoin);
|
||||
}
|
||||
}
|
||||
_applyMiterLimit(shape) {
|
||||
const miterLimit = shape.attrs.miterLimit;
|
||||
if (miterLimit != null) {
|
||||
this.setAttr('miterLimit', miterLimit);
|
||||
}
|
||||
}
|
||||
setAttr(attr, val) {
|
||||
this._context[attr] = val;
|
||||
}
|
||||
/**
|
||||
* arc function.
|
||||
* @method
|
||||
* @name Konva.Context#arc
|
||||
*/
|
||||
arc(x, y, radius, startAngle, endAngle, counterClockwise) {
|
||||
this._context.arc(x, y, radius, startAngle, endAngle, counterClockwise);
|
||||
}
|
||||
/**
|
||||
* arcTo function.
|
||||
* @method
|
||||
* @name Konva.Context#arcTo
|
||||
*
|
||||
*/
|
||||
arcTo(x1, y1, x2, y2, radius) {
|
||||
this._context.arcTo(x1, y1, x2, y2, radius);
|
||||
}
|
||||
/**
|
||||
* beginPath function.
|
||||
* @method
|
||||
* @name Konva.Context#beginPath
|
||||
*/
|
||||
beginPath() {
|
||||
this._context.beginPath();
|
||||
}
|
||||
/**
|
||||
* bezierCurveTo function.
|
||||
* @method
|
||||
* @name Konva.Context#bezierCurveTo
|
||||
*/
|
||||
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
|
||||
this._context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
|
||||
}
|
||||
/**
|
||||
* clearRect function.
|
||||
* @method
|
||||
* @name Konva.Context#clearRect
|
||||
*/
|
||||
clearRect(x, y, width, height) {
|
||||
this._context.clearRect(x, y, width, height);
|
||||
}
|
||||
clip(...args) {
|
||||
this._context.clip.apply(this._context, args);
|
||||
}
|
||||
/**
|
||||
* closePath function.
|
||||
* @method
|
||||
* @name Konva.Context#closePath
|
||||
*/
|
||||
closePath() {
|
||||
this._context.closePath();
|
||||
}
|
||||
/**
|
||||
* createImageData function.
|
||||
* @method
|
||||
* @name Konva.Context#createImageData
|
||||
*/
|
||||
createImageData(width, height) {
|
||||
const a = arguments;
|
||||
if (a.length === 2) {
|
||||
return this._context.createImageData(width, height);
|
||||
}
|
||||
else if (a.length === 1) {
|
||||
return this._context.createImageData(width);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* createLinearGradient function.
|
||||
* @method
|
||||
* @name Konva.Context#createLinearGradient
|
||||
*/
|
||||
createLinearGradient(x0, y0, x1, y1) {
|
||||
return this._context.createLinearGradient(x0, y0, x1, y1);
|
||||
}
|
||||
/**
|
||||
* createPattern function.
|
||||
* @method
|
||||
* @name Konva.Context#createPattern
|
||||
*/
|
||||
createPattern(image, repetition) {
|
||||
return this._context.createPattern(image, repetition);
|
||||
}
|
||||
/**
|
||||
* createRadialGradient function.
|
||||
* @method
|
||||
* @name Konva.Context#createRadialGradient
|
||||
*/
|
||||
createRadialGradient(x0, y0, r0, x1, y1, r1) {
|
||||
return this._context.createRadialGradient(x0, y0, r0, x1, y1, r1);
|
||||
}
|
||||
/**
|
||||
* drawImage function.
|
||||
* @method
|
||||
* @name Konva.Context#drawImage
|
||||
*/
|
||||
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) {
|
||||
// this._context.drawImage(...arguments);
|
||||
const a = arguments, _context = this._context;
|
||||
if (a.length === 3) {
|
||||
_context.drawImage(image, sx, sy);
|
||||
}
|
||||
else if (a.length === 5) {
|
||||
_context.drawImage(image, sx, sy, sWidth, sHeight);
|
||||
}
|
||||
else if (a.length === 9) {
|
||||
_context.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* ellipse function.
|
||||
* @method
|
||||
* @name Konva.Context#ellipse
|
||||
*/
|
||||
ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) {
|
||||
this._context.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise);
|
||||
}
|
||||
/**
|
||||
* isPointInPath function.
|
||||
* @method
|
||||
* @name Konva.Context#isPointInPath
|
||||
*/
|
||||
isPointInPath(x, y, path, fillRule) {
|
||||
if (path) {
|
||||
return this._context.isPointInPath(path, x, y, fillRule);
|
||||
}
|
||||
return this._context.isPointInPath(x, y, fillRule);
|
||||
}
|
||||
fill(...args) {
|
||||
// this._context.fill();
|
||||
this._context.fill.apply(this._context, args);
|
||||
}
|
||||
/**
|
||||
* fillRect function.
|
||||
* @method
|
||||
* @name Konva.Context#fillRect
|
||||
*/
|
||||
fillRect(x, y, width, height) {
|
||||
this._context.fillRect(x, y, width, height);
|
||||
}
|
||||
/**
|
||||
* strokeRect function.
|
||||
* @method
|
||||
* @name Konva.Context#strokeRect
|
||||
*/
|
||||
strokeRect(x, y, width, height) {
|
||||
this._context.strokeRect(x, y, width, height);
|
||||
}
|
||||
/**
|
||||
* fillText function.
|
||||
* @method
|
||||
* @name Konva.Context#fillText
|
||||
*/
|
||||
fillText(text, x, y, maxWidth) {
|
||||
if (maxWidth) {
|
||||
this._context.fillText(text, x, y, maxWidth);
|
||||
}
|
||||
else {
|
||||
this._context.fillText(text, x, y);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* measureText function.
|
||||
* @method
|
||||
* @name Konva.Context#measureText
|
||||
*/
|
||||
measureText(text) {
|
||||
return this._context.measureText(text);
|
||||
}
|
||||
/**
|
||||
* getImageData function.
|
||||
* @method
|
||||
* @name Konva.Context#getImageData
|
||||
*/
|
||||
getImageData(sx, sy, sw, sh) {
|
||||
return this._context.getImageData(sx, sy, sw, sh);
|
||||
}
|
||||
/**
|
||||
* lineTo function.
|
||||
* @method
|
||||
* @name Konva.Context#lineTo
|
||||
*/
|
||||
lineTo(x, y) {
|
||||
this._context.lineTo(x, y);
|
||||
}
|
||||
/**
|
||||
* moveTo function.
|
||||
* @method
|
||||
* @name Konva.Context#moveTo
|
||||
*/
|
||||
moveTo(x, y) {
|
||||
this._context.moveTo(x, y);
|
||||
}
|
||||
/**
|
||||
* rect function.
|
||||
* @method
|
||||
* @name Konva.Context#rect
|
||||
*/
|
||||
rect(x, y, width, height) {
|
||||
this._context.rect(x, y, width, height);
|
||||
}
|
||||
/**
|
||||
* roundRect function.
|
||||
* @method
|
||||
* @name Konva.Context#roundRect
|
||||
*/
|
||||
roundRect(x, y, width, height, radii) {
|
||||
this._context.roundRect(x, y, width, height, radii);
|
||||
}
|
||||
/**
|
||||
* putImageData function.
|
||||
* @method
|
||||
* @name Konva.Context#putImageData
|
||||
*/
|
||||
putImageData(imageData, dx, dy) {
|
||||
this._context.putImageData(imageData, dx, dy);
|
||||
}
|
||||
/**
|
||||
* quadraticCurveTo function.
|
||||
* @method
|
||||
* @name Konva.Context#quadraticCurveTo
|
||||
*/
|
||||
quadraticCurveTo(cpx, cpy, x, y) {
|
||||
this._context.quadraticCurveTo(cpx, cpy, x, y);
|
||||
}
|
||||
/**
|
||||
* restore function.
|
||||
* @method
|
||||
* @name Konva.Context#restore
|
||||
*/
|
||||
restore() {
|
||||
this._context.restore();
|
||||
}
|
||||
/**
|
||||
* rotate function.
|
||||
* @method
|
||||
* @name Konva.Context#rotate
|
||||
*/
|
||||
rotate(angle) {
|
||||
this._context.rotate(angle);
|
||||
}
|
||||
/**
|
||||
* save function.
|
||||
* @method
|
||||
* @name Konva.Context#save
|
||||
*/
|
||||
save() {
|
||||
this._context.save();
|
||||
}
|
||||
/**
|
||||
* scale function.
|
||||
* @method
|
||||
* @name Konva.Context#scale
|
||||
*/
|
||||
scale(x, y) {
|
||||
this._context.scale(x, y);
|
||||
}
|
||||
/**
|
||||
* setLineDash function.
|
||||
* @method
|
||||
* @name Konva.Context#setLineDash
|
||||
*/
|
||||
setLineDash(segments) {
|
||||
this._context.setLineDash(segments);
|
||||
}
|
||||
/**
|
||||
* getLineDash function.
|
||||
* @method
|
||||
* @name Konva.Context#getLineDash
|
||||
*/
|
||||
getLineDash() {
|
||||
return this._context.getLineDash();
|
||||
}
|
||||
/**
|
||||
* setTransform function.
|
||||
* @method
|
||||
* @name Konva.Context#setTransform
|
||||
*/
|
||||
setTransform(a, b, c, d, e, f) {
|
||||
this._context.setTransform(a, b, c, d, e, f);
|
||||
}
|
||||
/**
|
||||
* stroke function.
|
||||
* @method
|
||||
* @name Konva.Context#stroke
|
||||
*/
|
||||
stroke(path2d) {
|
||||
if (path2d) {
|
||||
this._context.stroke(path2d);
|
||||
}
|
||||
else {
|
||||
this._context.stroke();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* strokeText function.
|
||||
* @method
|
||||
* @name Konva.Context#strokeText
|
||||
*/
|
||||
strokeText(text, x, y, maxWidth) {
|
||||
this._context.strokeText(text, x, y, maxWidth);
|
||||
}
|
||||
/**
|
||||
* transform function.
|
||||
* @method
|
||||
* @name Konva.Context#transform
|
||||
*/
|
||||
transform(a, b, c, d, e, f) {
|
||||
this._context.transform(a, b, c, d, e, f);
|
||||
}
|
||||
/**
|
||||
* translate function.
|
||||
* @method
|
||||
* @name Konva.Context#translate
|
||||
*/
|
||||
translate(x, y) {
|
||||
this._context.translate(x, y);
|
||||
}
|
||||
_enableTrace() {
|
||||
let that = this, len = CONTEXT_METHODS.length, origSetter = this.setAttr, n, args;
|
||||
// to prevent creating scope function at each loop
|
||||
const func = function (methodName) {
|
||||
let origMethod = that[methodName], ret;
|
||||
that[methodName] = function () {
|
||||
args = simplifyArray(Array.prototype.slice.call(arguments, 0));
|
||||
ret = origMethod.apply(that, arguments);
|
||||
that._trace({
|
||||
method: methodName,
|
||||
args: args,
|
||||
});
|
||||
return ret;
|
||||
};
|
||||
};
|
||||
// methods
|
||||
for (n = 0; n < len; n++) {
|
||||
func(CONTEXT_METHODS[n]);
|
||||
}
|
||||
// attrs
|
||||
that.setAttr = function () {
|
||||
origSetter.apply(that, arguments);
|
||||
const prop = arguments[0];
|
||||
let val = arguments[1];
|
||||
if (prop === 'shadowOffsetX' ||
|
||||
prop === 'shadowOffsetY' ||
|
||||
prop === 'shadowBlur') {
|
||||
val = val / this.canvas.getPixelRatio();
|
||||
}
|
||||
that._trace({
|
||||
property: prop,
|
||||
val: val,
|
||||
});
|
||||
};
|
||||
}
|
||||
_applyGlobalCompositeOperation(node) {
|
||||
const op = node.attrs.globalCompositeOperation;
|
||||
const def = !op || op === 'source-over';
|
||||
if (!def) {
|
||||
this.setAttr('globalCompositeOperation', op);
|
||||
}
|
||||
}
|
||||
}
|
||||
CONTEXT_PROPERTIES.forEach(function (prop) {
|
||||
Object.defineProperty(Context.prototype, prop, {
|
||||
get() {
|
||||
return this._context[prop];
|
||||
},
|
||||
set(val) {
|
||||
this._context[prop] = val;
|
||||
},
|
||||
});
|
||||
});
|
||||
export class SceneContext extends Context {
|
||||
constructor(canvas, { willReadFrequently = false } = {}) {
|
||||
super(canvas);
|
||||
this._context = canvas._canvas.getContext('2d', {
|
||||
willReadFrequently,
|
||||
});
|
||||
}
|
||||
_getFillPattern(shape) {
|
||||
// node-canvas exposes pattern smoothing through patternQuality.
|
||||
const context = this._context;
|
||||
if ('patternQuality' in context) {
|
||||
context.patternQuality = context.imageSmoothingEnabled
|
||||
? 'good'
|
||||
: 'nearest';
|
||||
}
|
||||
return shape._getFillPattern();
|
||||
}
|
||||
_getFillStyle(shape) {
|
||||
const color = shape.fill();
|
||||
const priority = shape.fillPriority();
|
||||
if (color && priority === 'color')
|
||||
return color;
|
||||
const pattern = shape.fillPatternImage();
|
||||
if (pattern && priority === 'pattern')
|
||||
return this._getFillPattern(shape);
|
||||
const linear = shape.fillLinearGradientColorStops();
|
||||
if (linear && priority === 'linear-gradient')
|
||||
return shape._getLinearGradient();
|
||||
const radial = shape.fillRadialGradientColorStops();
|
||||
if (radial && priority === 'radial-gradient')
|
||||
return shape._getRadialGradient();
|
||||
if (color)
|
||||
return color;
|
||||
if (pattern)
|
||||
return this._getFillPattern(shape);
|
||||
if (linear)
|
||||
return shape._getLinearGradient();
|
||||
if (radial)
|
||||
return shape._getRadialGradient();
|
||||
}
|
||||
_fill(shape) {
|
||||
const style = this._getFillStyle(shape);
|
||||
if (style !== undefined) {
|
||||
this.setAttr('fillStyle', style);
|
||||
shape._fillFunc(this);
|
||||
}
|
||||
}
|
||||
_strokeLinearGradient(shape) {
|
||||
let start = shape.getStrokeLinearGradientStartPoint(), end = shape.getStrokeLinearGradientEndPoint();
|
||||
if (!shape.getStrokeScaleEnabled()) {
|
||||
const { a, b, c, d, e, f } = this._context.getTransform();
|
||||
const transform = new Transform([a, b, c, d, e, f]);
|
||||
const ratio = this.canvas.getPixelRatio();
|
||||
const dx = end.x - start.x;
|
||||
const dy = end.y - start.y;
|
||||
// Gradient colors follow the inverse-transposed direction. Transforming
|
||||
// the endpoints alone distorts them under skew or nonuniform scaling.
|
||||
const nx = d * dx - b * dy;
|
||||
const ny = a * dy - c * dx;
|
||||
const lengthSquared = nx * nx + ny * ny;
|
||||
const scale = lengthSquared
|
||||
? ((dx * dx + dy * dy) * (a * d - b * c)) / lengthSquared
|
||||
: 0;
|
||||
start = transform.point(start);
|
||||
end = { x: start.x + nx * scale, y: start.y + ny * scale };
|
||||
start = { x: start.x / ratio, y: start.y / ratio };
|
||||
end = { x: end.x / ratio, y: end.y / ratio };
|
||||
}
|
||||
const colorStops = shape.getStrokeLinearGradientColorStops(), grd = this.createLinearGradient(start.x, start.y, end.x, end.y);
|
||||
if (colorStops) {
|
||||
// build color stops
|
||||
for (let n = 0; n < colorStops.length; n += 2) {
|
||||
grd.addColorStop(colorStops[n], colorStops[n + 1]);
|
||||
}
|
||||
this.setAttr('strokeStyle', grd);
|
||||
}
|
||||
}
|
||||
_applyStrokeStyle(shape) {
|
||||
if (shape.strokeLinearGradientColorStops()) {
|
||||
this._strokeLinearGradient(shape);
|
||||
}
|
||||
else {
|
||||
this.setAttr('strokeStyle', shape.stroke());
|
||||
}
|
||||
}
|
||||
_stroke(shape) {
|
||||
const dash = shape.dash(),
|
||||
// ignore strokeScaleEnabled for Text
|
||||
strokeScaleEnabled = shape.getStrokeScaleEnabled();
|
||||
if (!strokeScaleEnabled) {
|
||||
this.save();
|
||||
}
|
||||
this._applyLineCap(shape);
|
||||
if (dash && shape.dashEnabled()) {
|
||||
this.setLineDash(dash);
|
||||
this.setAttr('lineDashOffset', shape.dashOffset());
|
||||
}
|
||||
this.setAttr('lineWidth', shape.strokeWidth());
|
||||
const shadowColor = this.shadowColor;
|
||||
const shadowForStrokeEnabled = shape.getShadowForStrokeEnabled();
|
||||
if (!shadowForStrokeEnabled) {
|
||||
this.setAttr('shadowColor', 'rgba(0,0,0,0)');
|
||||
}
|
||||
this._applyStrokeStyle(shape);
|
||||
// Resolve the gradient in local coordinates before drawing an unscaled stroke.
|
||||
if (!strokeScaleEnabled) {
|
||||
const pixelRatio = this.getCanvas().getPixelRatio();
|
||||
this.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
|
||||
}
|
||||
try {
|
||||
shape._strokeFunc(this);
|
||||
}
|
||||
finally {
|
||||
if (!shadowForStrokeEnabled) {
|
||||
this.setAttr('shadowColor', shadowColor);
|
||||
}
|
||||
if (!strokeScaleEnabled) {
|
||||
this.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
_applyShadow(shape) {
|
||||
var _a;
|
||||
const color = (_a = shape.getShadowRGBA()) !== null && _a !== void 0 ? _a : 'black', blur = shape.getShadowBlur(), offset = shape.getShadowOffset(), scale = shape.getAbsoluteScale(), ratio = this.canvas.getPixelRatio(), scaleX = scale.x * ratio, scaleY = scale.y * ratio;
|
||||
this.setAttr('shadowColor', color);
|
||||
this.setAttr('shadowBlur', blur * Math.min(Math.abs(scaleX), Math.abs(scaleY)));
|
||||
this.setAttr('shadowOffsetX', offset.x * scaleX);
|
||||
this.setAttr('shadowOffsetY', offset.y * scaleY);
|
||||
}
|
||||
}
|
||||
export class HitContext extends Context {
|
||||
constructor(canvas) {
|
||||
super(canvas);
|
||||
this._context = canvas._canvas.getContext('2d', {
|
||||
willReadFrequently: true,
|
||||
});
|
||||
}
|
||||
_fill(shape) {
|
||||
this.save();
|
||||
this.setAttr('fillStyle', shape.colorKey);
|
||||
shape._fillFuncHit(this);
|
||||
this.restore();
|
||||
}
|
||||
strokeShape(shape) {
|
||||
if (shape.hasHitStroke()) {
|
||||
this._stroke(shape);
|
||||
}
|
||||
}
|
||||
_stroke(shape) {
|
||||
// ignore strokeScaleEnabled for Text
|
||||
const strokeScaleEnabled = shape.getStrokeScaleEnabled();
|
||||
if (!strokeScaleEnabled) {
|
||||
this.save();
|
||||
const pixelRatio = this.getCanvas().getPixelRatio();
|
||||
this.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
|
||||
}
|
||||
this._applyLineCap(shape);
|
||||
const hitStrokeWidth = shape.hitStrokeWidth();
|
||||
const strokeWidth = hitStrokeWidth === 'auto' ? shape.strokeWidth() : hitStrokeWidth;
|
||||
this.setAttr('lineWidth', strokeWidth);
|
||||
this.setAttr('strokeStyle', shape.colorKey);
|
||||
shape._strokeFuncHit(this);
|
||||
if (!strokeScaleEnabled) {
|
||||
this.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export { Konva } from './_CoreInternals.ts';
|
||||
import { Konva } from './_CoreInternals.ts';
|
||||
export default Konva;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// enter file of limited Konva version with only core functions
|
||||
export { Konva } from "./_CoreInternals.js";
|
||||
import { Konva } from "./_CoreInternals.js";
|
||||
export default Konva;
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import type { Node } from './Node.ts';
|
||||
import type { Stage } from './Stage.ts';
|
||||
import type { Vector2d } from './types.ts';
|
||||
type DragElement = {
|
||||
node: Node;
|
||||
startPointerPos: Vector2d;
|
||||
offset: Vector2d;
|
||||
pointerId?: number;
|
||||
pointerEventType?: Stage['_pointerEventType'];
|
||||
lastPos?: Vector2d;
|
||||
startEvent?: any;
|
||||
dragStatus: 'ready' | 'dragging' | 'stopped';
|
||||
};
|
||||
export declare const DD: {
|
||||
readonly isDragging: boolean;
|
||||
readonly node: Node<import("./Node.ts").NodeConfig> | undefined;
|
||||
_dragElements: Map<number, DragElement>;
|
||||
_listenToWindow(win: Window): void;
|
||||
_batchEvents(callback: () => void, win?: Window): void;
|
||||
_drag(evt: any, win?: Window): void;
|
||||
_endDragBefore(evt?: any, win?: Window, only?: DragElement): void;
|
||||
_endDragAfter(evt: any, only?: DragElement): void;
|
||||
};
|
||||
export {};
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
import { Util } from "./Util.js";
|
||||
export const DD = {
|
||||
get isDragging() {
|
||||
let flag = false;
|
||||
DD._dragElements.forEach((elem) => {
|
||||
if (elem.dragStatus === 'dragging') {
|
||||
flag = true;
|
||||
}
|
||||
});
|
||||
return flag;
|
||||
},
|
||||
get node() {
|
||||
let node;
|
||||
for (const elem of DD._dragElements.values()) {
|
||||
if (elem.dragStatus === 'dragging')
|
||||
return elem.node;
|
||||
node = elem.node;
|
||||
}
|
||||
// isDragReady() also uses this getter before movement starts.
|
||||
return node;
|
||||
},
|
||||
_dragElements: new Map(),
|
||||
// Konva is imported into one window, but a stage may be rendered in another
|
||||
// one: an iframe, or a window opened with `window.open`. Such a window sends
|
||||
// its pointer events to itself, so `Konva.Stage` asks for every window it is
|
||||
// rendered in. The handlers get the window they listen to, as only that
|
||||
// window has pointer positions the stages of that window can use
|
||||
_listenToWindow(win) {
|
||||
const endDragBefore = (evt) => DD._endDragBefore(evt, win);
|
||||
const drag = (evt) => DD._batchEvents(() => DD._drag(evt, win), win);
|
||||
const endDragAfter = (evt) => DD._batchEvents(() => DD._endDragAfter(evt));
|
||||
win.addEventListener('mouseup', endDragBefore, true);
|
||||
win.addEventListener('touchend', endDragBefore, true);
|
||||
// add touchcancel to fix this: https://github.com/konvajs/konva/issues/1843
|
||||
win.addEventListener('touchcancel', endDragBefore, true);
|
||||
win.addEventListener('mousemove', drag);
|
||||
win.addEventListener('touchmove', drag);
|
||||
win.addEventListener('mouseup', endDragAfter, false);
|
||||
win.addEventListener('touchend', endDragAfter, false);
|
||||
win.addEventListener('touchcancel', endDragAfter, false);
|
||||
},
|
||||
_batchEvents(callback, win) {
|
||||
if (!DD._dragElements.size)
|
||||
return;
|
||||
// One scope per integration, even when several stages use the same callback.
|
||||
// Keep all positions and handlers in the original order across those stages.
|
||||
const batches = new Set();
|
||||
for (const { node } of DD._dragElements.values()) {
|
||||
const stage = node.getStage();
|
||||
if (!stage || (win && stage._getOwnerWindow() !== win))
|
||||
continue;
|
||||
const batch = stage.eventBatchFunc();
|
||||
if (batch)
|
||||
batches.add(batch);
|
||||
}
|
||||
let run = callback;
|
||||
for (const batch of batches) {
|
||||
const next = run;
|
||||
run = () => Util._batchEvents(batch, next);
|
||||
}
|
||||
run();
|
||||
},
|
||||
// methods
|
||||
_drag(evt, win) {
|
||||
const nodesToFireEvents = [];
|
||||
// reading the positions forces a layout, so do it once per stage
|
||||
const positioned = new Set();
|
||||
DD._dragElements.forEach((elem, key) => {
|
||||
const { node } = elem;
|
||||
// we need to find pointer relative to that node
|
||||
const stage = node.getStage();
|
||||
// a pointer position of another window is relative to another viewport,
|
||||
// it would drag the node to a random place
|
||||
if (win && stage._getOwnerWindow() !== win) {
|
||||
return;
|
||||
}
|
||||
if (!positioned.has(stage)) {
|
||||
stage.setPointersPositions(evt);
|
||||
positioned.add(stage);
|
||||
}
|
||||
if (elem.pointerEventType &&
|
||||
elem.pointerEventType !== stage._pointerEventType) {
|
||||
return;
|
||||
}
|
||||
// it is possible that user call startDrag without any event
|
||||
// it that case we need to detect first movable pointer and attach it into the node
|
||||
if (elem.pointerId === undefined) {
|
||||
elem.pointerId = Util._getFirstPointerId(evt);
|
||||
elem.pointerEventType = stage._pointerEventType;
|
||||
if (elem.dragStatus === 'dragging') {
|
||||
stage._cancelClick(elem.pointerId, elem.pointerEventType);
|
||||
}
|
||||
}
|
||||
const pos = stage._changedPointerPositions.find((pos) => pos.id === elem.pointerId);
|
||||
// not related pointer
|
||||
if (!pos) {
|
||||
return;
|
||||
}
|
||||
if (elem.dragStatus !== 'dragging') {
|
||||
const dragDistance = node.dragDistance();
|
||||
const distance = Math.max(Math.abs(pos.x - elem.startPointerPos.x), Math.abs(pos.y - elem.startPointerPos.y));
|
||||
if (distance < dragDistance) {
|
||||
return;
|
||||
}
|
||||
node.startDrag({ evt });
|
||||
// a user can stop dragging inside `dragstart`
|
||||
if (!node.isDragging()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
node._setDragPosition(evt, elem);
|
||||
nodesToFireEvents.push(node);
|
||||
});
|
||||
// call dragmove only after ALL positions are changed
|
||||
nodesToFireEvents.forEach((node) => {
|
||||
// node may have been destroyed during a previous dragmove handler
|
||||
if (!node.getStage()) {
|
||||
return;
|
||||
}
|
||||
node.fire('dragmove', {
|
||||
type: 'dragmove',
|
||||
target: node,
|
||||
evt: evt,
|
||||
}, true);
|
||||
});
|
||||
},
|
||||
// dragBefore and dragAfter allows us to set correct order of events
|
||||
// setup all in dragbefore, and stop dragging only after pointerup triggered.
|
||||
_endDragBefore(evt, win, only) {
|
||||
const drawNodes = [];
|
||||
const positioned = new Set();
|
||||
DD._dragElements.forEach((elem, key) => {
|
||||
const { node } = elem;
|
||||
// node.stopDrag() ends only the drags of the same gesture: a Transformer
|
||||
// drags its other nodes with the same pointer, another pointer is its own
|
||||
if (only && elem !== only && elem.pointerId !== only.pointerId) {
|
||||
return;
|
||||
}
|
||||
// we need to find pointer relative to that node
|
||||
const stage = node.getStage();
|
||||
// a pointer released in another window ends the drag too - the pointer
|
||||
// is up everywhere - but its position is not used for this stage
|
||||
if (evt &&
|
||||
!positioned.has(stage) &&
|
||||
(!win || stage._getOwnerWindow() === win)) {
|
||||
stage.setPointersPositions(evt);
|
||||
positioned.add(stage);
|
||||
}
|
||||
if (!only) {
|
||||
if (evt &&
|
||||
elem.pointerEventType &&
|
||||
elem.pointerEventType !== Util._getEventType(evt.type)) {
|
||||
return;
|
||||
}
|
||||
// a drag started without an event (node.startDrag()) that has not
|
||||
// moved yet has no pointer: any release ends it
|
||||
const released = elem.pointerId === undefined ||
|
||||
stage._changedPointerPositions.some((pos) => pos.id === elem.pointerId);
|
||||
// that pointer is not related: a "ready" element of another pointer
|
||||
// waits for its own release
|
||||
if (!released) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (elem.dragStatus === 'ready') {
|
||||
DD._dragElements.delete(key);
|
||||
}
|
||||
else {
|
||||
// if a node is stopped manually we still need to reset events:
|
||||
stage._cancelClick(elem.pointerId, elem.pointerEventType);
|
||||
elem.dragStatus = 'stopped';
|
||||
// a node that has not moved needs no redraw
|
||||
const drawNode = node.getLayer() || stage;
|
||||
if (drawNode && drawNodes.indexOf(drawNode) === -1) {
|
||||
drawNodes.push(drawNode);
|
||||
}
|
||||
}
|
||||
});
|
||||
// draw in a sync way
|
||||
// because mousemove event may trigger BEFORE batch draw is called
|
||||
// but as we have not hit canvas updated yet, it will trigger incorrect mouseover/mouseout events
|
||||
drawNodes.forEach((drawNode) => {
|
||||
drawNode.draw();
|
||||
});
|
||||
},
|
||||
_endDragAfter(evt, only) {
|
||||
DD._dragElements.forEach((elem, key) => {
|
||||
if (elem.dragStatus !== 'stopped' ||
|
||||
(only && elem !== only && elem.pointerId !== only.pointerId)) {
|
||||
return;
|
||||
}
|
||||
elem.node.fire('dragend', {
|
||||
type: 'dragend',
|
||||
target: elem.node,
|
||||
evt: evt,
|
||||
}, true);
|
||||
// a dragend handler may have started a new drag of the node
|
||||
if (elem.dragStatus !== 'dragging') {
|
||||
DD._dragElements.delete(key);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
import type { Node } from './Node.ts';
|
||||
import type { GetSet } from './types.ts';
|
||||
/**
|
||||
* Enforces that a type is a string.
|
||||
*/
|
||||
type EnforceString<T> = T extends string ? T : never;
|
||||
/**
|
||||
* Represents a class.
|
||||
*/
|
||||
type Constructor = abstract new (...args: any) => any;
|
||||
/**
|
||||
* An attribute of an instance of the provided class. Attributes names be strings.
|
||||
*/
|
||||
type Attr<T extends Constructor> = EnforceString<keyof InstanceType<T>>;
|
||||
/**
|
||||
* A function that is called after a setter is called.
|
||||
*/
|
||||
type AfterFunc<T extends Constructor> = (this: InstanceType<T>) => void;
|
||||
/**
|
||||
* Extracts the type a GetSet returns. The type the setter accepts can be wider,
|
||||
* so it is matched with `any` instead of being inferred from the getter.
|
||||
*/
|
||||
type ExtractGetSet<T> = T extends GetSet<infer U, any, any> ? U : never;
|
||||
/**
|
||||
* Extracts the type of a GetSet class attribute.
|
||||
*/
|
||||
type Value<T extends Constructor, U extends Attr<T>> = ExtractGetSet<InstanceType<T>[U]>;
|
||||
/**
|
||||
* A function that validates a value.
|
||||
*/
|
||||
type ValidatorFunc<T> = (val: T, attr: string) => T;
|
||||
/**
|
||||
* Extracts the "components" (keys) of a GetSet value. The value must be an object.
|
||||
*/
|
||||
type ExtractComponents<T extends Constructor, U extends Attr<T>> = Value<T, U> extends Record<string, any> ? EnforceString<keyof Value<T, U>>[] : never;
|
||||
export declare const Factory: {
|
||||
addGetterSetter<T extends Constructor, U extends Attr<T>>(constructor: T, attr: U, def?: Value<T, U>, validator?: ValidatorFunc<Value<T, U>>, after?: AfterFunc<T>): void;
|
||||
addGetter<T extends Constructor, U extends Attr<T>>(constructor: T, attr: U, def?: Value<T, U>): void;
|
||||
addSetter<T extends Constructor, U extends Attr<T>>(constructor: T, attr: U, validator?: ValidatorFunc<Value<T, U>>, after?: AfterFunc<T>): void;
|
||||
overWriteSetter<T extends Constructor, U extends Attr<T>>(constructor: T, attr: U, validator?: ValidatorFunc<Value<T, U>>, after?: AfterFunc<T>): void;
|
||||
addComponentsGetterSetter<T extends Constructor, U extends Attr<T>>(constructor: T, attr: U, components: ExtractComponents<T, U>, validator?: ValidatorFunc<Value<T, U>>, after?: AfterFunc<T>): void;
|
||||
addOverloadedGetterSetter<T extends Constructor, U extends Attr<T>>(constructor: T, attr: U): void;
|
||||
backCompat<T extends Constructor>(constructor: T, methods: Record<string, string>): void;
|
||||
afterSetFilter(this: Node): void;
|
||||
};
|
||||
export {};
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
import { Util } from "./Util.js";
|
||||
import { getComponentValidator } from "./Validators.js";
|
||||
const GET = 'get';
|
||||
const SET = 'set';
|
||||
export const Factory = {
|
||||
addGetterSetter(constructor, attr, def, validator, after) {
|
||||
Factory.addGetter(constructor, attr, def);
|
||||
Factory.addSetter(constructor, attr, validator, after);
|
||||
Factory.addOverloadedGetterSetter(constructor, attr);
|
||||
},
|
||||
addGetter(constructor, attr, def) {
|
||||
const method = GET + Util._capitalize(attr);
|
||||
// array defaults are shared between instances, so hand out a copy
|
||||
const isArr = Array.isArray(def);
|
||||
const getter = function () {
|
||||
const val = this.attrs[attr];
|
||||
return val === undefined ? (isArr ? def.slice() : def) : val;
|
||||
};
|
||||
// toObject() compares an attr with the default of the generated getter
|
||||
getter._def = def;
|
||||
constructor.prototype[method] = constructor.prototype[method] || getter;
|
||||
},
|
||||
addSetter(constructor, attr, validator, after) {
|
||||
const method = SET + Util._capitalize(attr);
|
||||
if (!constructor.prototype[method]) {
|
||||
Factory.overWriteSetter(constructor, attr, validator, after);
|
||||
}
|
||||
},
|
||||
overWriteSetter(constructor, attr, validator, after) {
|
||||
const method = SET + Util._capitalize(attr);
|
||||
constructor.prototype[method] = function (val) {
|
||||
if (validator && val !== undefined && val !== null) {
|
||||
val = validator.call(this, val, attr);
|
||||
}
|
||||
this._setAttr(attr, val);
|
||||
if (after) {
|
||||
after.call(this);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
},
|
||||
addComponentsGetterSetter(constructor, attr, components, validator, after) {
|
||||
const len = components.length, capitalize = Util._capitalize, getter = GET + capitalize(attr), setter = SET + capitalize(attr), keys = components.map((c) => attr + capitalize(c)),
|
||||
// built-in components have their own getters, e.g. getOffsetX
|
||||
getters = keys.map((key) => GET + capitalize(key));
|
||||
// getter
|
||||
constructor.prototype[getter] = function () {
|
||||
const ret = {};
|
||||
for (let n = 0; n < len; n++) {
|
||||
const get = this[getters[n]];
|
||||
ret[components[n]] = get ? get.call(this) : this.attrs[keys[n]];
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
const basicValidator = getComponentValidator(components);
|
||||
// setter
|
||||
constructor.prototype[setter] = function (val) {
|
||||
const oldVal = this[getter]();
|
||||
if (validator) {
|
||||
val = validator.call(this, val, attr);
|
||||
}
|
||||
if (basicValidator) {
|
||||
basicValidator.call(this, val, attr);
|
||||
}
|
||||
for (const key in val) {
|
||||
if (!val.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
this._setAttr(attr + capitalize(key), val[key]);
|
||||
}
|
||||
if (!val) {
|
||||
components.forEach((component) => {
|
||||
this._setAttr(attr + capitalize(component), undefined);
|
||||
});
|
||||
}
|
||||
this._fireChangeEvent(attr, oldVal, val);
|
||||
if (after) {
|
||||
after.call(this);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
Factory.addOverloadedGetterSetter(constructor, attr);
|
||||
},
|
||||
addOverloadedGetterSetter(constructor, attr) {
|
||||
const capitalizedAttr = Util._capitalize(attr), setter = SET + capitalizedAttr, getter = GET + capitalizedAttr;
|
||||
const accessor = function () {
|
||||
// setting
|
||||
if (arguments.length) {
|
||||
this[setter](arguments[0]);
|
||||
return this;
|
||||
}
|
||||
// getting
|
||||
return this[getter]();
|
||||
};
|
||||
constructor.prototype[attr] = accessor;
|
||||
},
|
||||
backCompat(constructor, methods) {
|
||||
Util.each(methods, function (oldMethodName, newMethodName) {
|
||||
const method = constructor.prototype[newMethodName];
|
||||
const oldGetter = GET + Util._capitalize(oldMethodName);
|
||||
const oldSetter = SET + Util._capitalize(oldMethodName);
|
||||
function deprecated() {
|
||||
Util.error('"' +
|
||||
oldMethodName +
|
||||
'" method is deprecated and will be removed soon. Use ""' +
|
||||
newMethodName +
|
||||
'" instead.');
|
||||
return method.apply(this, arguments);
|
||||
}
|
||||
constructor.prototype[oldMethodName] = deprecated;
|
||||
constructor.prototype[oldGetter] = deprecated;
|
||||
constructor.prototype[oldSetter] = deprecated;
|
||||
});
|
||||
},
|
||||
afterSetFilter() {
|
||||
this._filterUpToDate = false;
|
||||
},
|
||||
};
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { Layer } from './Layer.ts';
|
||||
/**
|
||||
* FastLayer constructor. **DEPRECATED!** Please use `Konva.Layer({ listening: false})` instead. Layers are tied to their own canvas element and are used
|
||||
* to contain shapes only. If you don't need node nesting, mouse and touch interactions,
|
||||
* or event pub/sub, you should use FastLayer instead of Layer to create your layers.
|
||||
* It renders about 2x faster than normal layers.
|
||||
*
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Layer
|
||||
@@containerParams
|
||||
* @example
|
||||
* var layer = new Konva.FastLayer();
|
||||
*/
|
||||
export declare class FastLayer extends Layer {
|
||||
constructor(attrs?: any);
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { Util } from "./Util.js";
|
||||
import { Layer } from "./Layer.js";
|
||||
import { _registerNode } from "./Global.js";
|
||||
/**
|
||||
* FastLayer constructor. **DEPRECATED!** Please use `Konva.Layer({ listening: false})` instead. Layers are tied to their own canvas element and are used
|
||||
* to contain shapes only. If you don't need node nesting, mouse and touch interactions,
|
||||
* or event pub/sub, you should use FastLayer instead of Layer to create your layers.
|
||||
* It renders about 2x faster than normal layers.
|
||||
*
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Layer
|
||||
@@containerParams
|
||||
* @example
|
||||
* var layer = new Konva.FastLayer();
|
||||
*/
|
||||
export class FastLayer extends Layer {
|
||||
constructor(attrs) {
|
||||
super(attrs);
|
||||
this.listening(false);
|
||||
Util.warn('Konva.FastLayer is deprecated. Please use "new Konva.Layer({ listening: false })" instead.');
|
||||
}
|
||||
}
|
||||
FastLayer.prototype.nodeType = 'FastLayer';
|
||||
_registerNode(FastLayer);
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
export declare const glob: any;
|
||||
export declare const Konva: {
|
||||
_global: any;
|
||||
version: string;
|
||||
isBrowser: boolean;
|
||||
isUnminified: boolean;
|
||||
dblClickWindow: number;
|
||||
getAngle(angle: number): number;
|
||||
enableTrace: boolean;
|
||||
pointerEventsEnabled: boolean;
|
||||
/**
|
||||
* Should Konva automatically update canvas on any changes. Default is true.
|
||||
* @property autoDrawEnabled
|
||||
* @default true
|
||||
* @name autoDrawEnabled
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* Konva.autoDrawEnabled = true;
|
||||
*/
|
||||
autoDrawEnabled: boolean;
|
||||
/**
|
||||
* Should we enable hit detection while dragging? For performance reasons, by default it is false.
|
||||
* But on some rare cases you want to see hit graph and check intersections. Just set it to true.
|
||||
* @property hitOnDragEnabled
|
||||
* @default false
|
||||
* @name hitOnDragEnabled
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* Konva.hitOnDragEnabled = true;
|
||||
*/
|
||||
hitOnDragEnabled: boolean;
|
||||
/**
|
||||
* Should we capture touch events and bind them to the touchstart target? That is how it works on DOM elements.
|
||||
* The case: we touchstart on div1, then touchmove out of that element into another element div2.
|
||||
* DOM will continue trigger touchmove events on div1 (not div2). Because events are "captured" into initial target.
|
||||
* By default Konva do not do that and will trigger touchmove on another element, while pointer is moving.
|
||||
* @property capturePointerEventsEnabled
|
||||
* @default false
|
||||
* @name capturePointerEventsEnabled
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* Konva.capturePointerEventsEnabled = true;
|
||||
*/
|
||||
capturePointerEventsEnabled: boolean;
|
||||
_renderBackend: string;
|
||||
/**
|
||||
* Use legacy text rendering. with "middle" baseline by default.
|
||||
* @property legacyTextRendering
|
||||
* @default false
|
||||
* @name legacyTextRendering
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* Konva.legacyTextRendering = true;
|
||||
*/
|
||||
legacyTextRendering: boolean;
|
||||
/**
|
||||
* Global pixel ratio configuration. KonvaJS automatically detect pixel ratio of current device.
|
||||
* But you may override such property, if you want to use your value. Set this value before any components initializations.
|
||||
* @property pixelRatio
|
||||
* @default window.devicePixelRatio || 1
|
||||
* @name pixelRatio
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* // before any Konva code:
|
||||
* Konva.pixelRatio = 1;
|
||||
*/
|
||||
pixelRatio: number;
|
||||
/**
|
||||
* Drag distance property. If you start to drag a node you may want to wait until pointer is moved to some distance from start point
|
||||
* on either axis, only then start dragging. Default is 3px.
|
||||
* @property dragDistance
|
||||
* @default 3
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* Konva.dragDistance = 10;
|
||||
*/
|
||||
dragDistance: number;
|
||||
/**
|
||||
* Use degree values for angle properties. You may set this property to false if you want to use radian values.
|
||||
* @property angleDeg
|
||||
* @default true
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* node.rotation(45); // 45 degrees
|
||||
* Konva.angleDeg = false;
|
||||
* node.rotation(Math.PI / 2); // PI/2 radian
|
||||
*/
|
||||
angleDeg: boolean;
|
||||
/**
|
||||
* Show warnings about wrong API usage (`Konva.Util.warn`). Errors are always printed
|
||||
* @property showWarnings
|
||||
* @default true
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* Konva.showWarnings = false;
|
||||
*/
|
||||
showWarnings: boolean;
|
||||
/**
|
||||
* Configure what mouse buttons can be used for drag and drop.
|
||||
* Default value is [0, 1] - left and middle mouse buttons.
|
||||
* @property dragButtons
|
||||
* @default [0, 1]
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* // enable left and right mouse buttons
|
||||
* Konva.dragButtons = [0, 2];
|
||||
*/
|
||||
dragButtons: number[];
|
||||
/**
|
||||
* returns whether or not drag and drop is currently active
|
||||
* @method
|
||||
* @memberof Konva
|
||||
*/
|
||||
isDragging(): boolean;
|
||||
/**
|
||||
* returns whether or not a Transformer is currently transforming a node
|
||||
* @method
|
||||
* @memberof Konva
|
||||
*/
|
||||
isTransforming(): boolean;
|
||||
/**
|
||||
* returns whether or not a drag and drop operation is ready, but may
|
||||
* not necessarily have started
|
||||
* @method
|
||||
* @memberof Konva
|
||||
*/
|
||||
isDragReady(): boolean;
|
||||
/**
|
||||
* Should Konva release canvas elements on destroy. Default is true.
|
||||
* Useful to avoid memory leak issues in Safari on macOS/iOS.
|
||||
* @property releaseCanvasOnDestroy
|
||||
* @default true
|
||||
* @name releaseCanvasOnDestroy
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* Konva.releaseCanvasOnDestroy = true;
|
||||
*/
|
||||
releaseCanvasOnDestroy: boolean;
|
||||
document: any;
|
||||
_injectGlobal(Konva: any): void;
|
||||
};
|
||||
export declare const _boundedShapes: Set<unknown>;
|
||||
export declare const _registerNode: (NodeClass: any, boundsCoverPaint?: boolean) => void;
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Konva JavaScript Framework v10.9.0
|
||||
* http://konvajs.org/
|
||||
* Licensed under the MIT
|
||||
* Date: @@date
|
||||
*
|
||||
* Original work Copyright (C) 2011 - 2013 by Eric Rowell (KineticJS)
|
||||
* Modified work Copyright (C) 2014 - present by Anton Lavrenov (Konva)
|
||||
*
|
||||
* @license
|
||||
*/
|
||||
const PI_OVER_180 = Math.PI / 180;
|
||||
/**
|
||||
* @namespace Konva
|
||||
*/
|
||||
function detectBrowser() {
|
||||
return (typeof window !== 'undefined' &&
|
||||
// browser case
|
||||
({}.toString.call(window) === '[object Window]' ||
|
||||
// electron case
|
||||
{}.toString.call(window) === '[object global]'));
|
||||
}
|
||||
export const glob = typeof global !== 'undefined'
|
||||
? global
|
||||
: typeof window !== 'undefined'
|
||||
? window
|
||||
: typeof WorkerGlobalScope !== 'undefined'
|
||||
? self
|
||||
: {};
|
||||
export const Konva = {
|
||||
_global: glob,
|
||||
version: '10.9.0',
|
||||
isBrowser: detectBrowser(),
|
||||
isUnminified: /param/.test(function (param) { }.toString()),
|
||||
dblClickWindow: 400,
|
||||
getAngle(angle) {
|
||||
return Konva.angleDeg ? angle * PI_OVER_180 : angle;
|
||||
},
|
||||
enableTrace: false,
|
||||
pointerEventsEnabled: true,
|
||||
/**
|
||||
* Should Konva automatically update canvas on any changes. Default is true.
|
||||
* @property autoDrawEnabled
|
||||
* @default true
|
||||
* @name autoDrawEnabled
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* Konva.autoDrawEnabled = true;
|
||||
*/
|
||||
autoDrawEnabled: true,
|
||||
/**
|
||||
* Should we enable hit detection while dragging? For performance reasons, by default it is false.
|
||||
* But on some rare cases you want to see hit graph and check intersections. Just set it to true.
|
||||
* @property hitOnDragEnabled
|
||||
* @default false
|
||||
* @name hitOnDragEnabled
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* Konva.hitOnDragEnabled = true;
|
||||
*/
|
||||
hitOnDragEnabled: false,
|
||||
/**
|
||||
* Should we capture touch events and bind them to the touchstart target? That is how it works on DOM elements.
|
||||
* The case: we touchstart on div1, then touchmove out of that element into another element div2.
|
||||
* DOM will continue trigger touchmove events on div1 (not div2). Because events are "captured" into initial target.
|
||||
* By default Konva do not do that and will trigger touchmove on another element, while pointer is moving.
|
||||
* @property capturePointerEventsEnabled
|
||||
* @default false
|
||||
* @name capturePointerEventsEnabled
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* Konva.capturePointerEventsEnabled = true;
|
||||
*/
|
||||
capturePointerEventsEnabled: false,
|
||||
_renderBackend: 'web', // web, node-canvas, skia-canvas
|
||||
/**
|
||||
* Use legacy text rendering. with "middle" baseline by default.
|
||||
* @property legacyTextRendering
|
||||
* @default false
|
||||
* @name legacyTextRendering
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* Konva.legacyTextRendering = true;
|
||||
*/
|
||||
legacyTextRendering: false,
|
||||
/**
|
||||
* Global pixel ratio configuration. KonvaJS automatically detect pixel ratio of current device.
|
||||
* But you may override such property, if you want to use your value. Set this value before any components initializations.
|
||||
* @property pixelRatio
|
||||
* @default window.devicePixelRatio || 1
|
||||
* @name pixelRatio
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* // before any Konva code:
|
||||
* Konva.pixelRatio = 1;
|
||||
*/
|
||||
pixelRatio: (typeof window !== 'undefined' && window.devicePixelRatio) || 1,
|
||||
/**
|
||||
* Drag distance property. If you start to drag a node you may want to wait until pointer is moved to some distance from start point
|
||||
* on either axis, only then start dragging. Default is 3px.
|
||||
* @property dragDistance
|
||||
* @default 3
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* Konva.dragDistance = 10;
|
||||
*/
|
||||
dragDistance: 3,
|
||||
/**
|
||||
* Use degree values for angle properties. You may set this property to false if you want to use radian values.
|
||||
* @property angleDeg
|
||||
* @default true
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* node.rotation(45); // 45 degrees
|
||||
* Konva.angleDeg = false;
|
||||
* node.rotation(Math.PI / 2); // PI/2 radian
|
||||
*/
|
||||
angleDeg: true,
|
||||
/**
|
||||
* Show warnings about wrong API usage (`Konva.Util.warn`). Errors are always printed
|
||||
* @property showWarnings
|
||||
* @default true
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* Konva.showWarnings = false;
|
||||
*/
|
||||
showWarnings: true,
|
||||
/**
|
||||
* Configure what mouse buttons can be used for drag and drop.
|
||||
* Default value is [0, 1] - left and middle mouse buttons.
|
||||
* @property dragButtons
|
||||
* @default [0, 1]
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* // enable left and right mouse buttons
|
||||
* Konva.dragButtons = [0, 2];
|
||||
*/
|
||||
dragButtons: [0, 1],
|
||||
/**
|
||||
* returns whether or not drag and drop is currently active
|
||||
* @method
|
||||
* @memberof Konva
|
||||
*/
|
||||
isDragging() {
|
||||
return Konva['DD'].isDragging;
|
||||
},
|
||||
/**
|
||||
* returns whether or not a Transformer is currently transforming a node
|
||||
* @method
|
||||
* @memberof Konva
|
||||
*/
|
||||
isTransforming() {
|
||||
var _a, _b;
|
||||
return (_b = (_a = Konva['Transformer']) === null || _a === void 0 ? void 0 : _a.isTransforming()) !== null && _b !== void 0 ? _b : false;
|
||||
},
|
||||
/**
|
||||
* returns whether or not a drag and drop operation is ready, but may
|
||||
* not necessarily have started
|
||||
* @method
|
||||
* @memberof Konva
|
||||
*/
|
||||
isDragReady() {
|
||||
return !!Konva['DD'].node;
|
||||
},
|
||||
/**
|
||||
* Should Konva release canvas elements on destroy. Default is true.
|
||||
* Useful to avoid memory leak issues in Safari on macOS/iOS.
|
||||
* @property releaseCanvasOnDestroy
|
||||
* @default true
|
||||
* @name releaseCanvasOnDestroy
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* Konva.releaseCanvasOnDestroy = true;
|
||||
*/
|
||||
releaseCanvasOnDestroy: true,
|
||||
document: glob.document,
|
||||
// insert Konva into global namespace (window)
|
||||
// it is required for npm packages
|
||||
_injectGlobal(Konva) {
|
||||
if (typeof glob.Konva !== 'undefined') {
|
||||
console.error('Several Konva instances detected. It is not recommended to use multiple Konva instances in the same environment.');
|
||||
}
|
||||
glob.Konva = Konva;
|
||||
},
|
||||
};
|
||||
// Konva's own shapes whose bounds cover everything they paint. Plugins can
|
||||
// import _registerNode, so they opt in explicitly.
|
||||
export const _boundedShapes = new Set();
|
||||
export const _registerNode = (NodeClass, boundsCoverPaint = false) => {
|
||||
Konva[NodeClass.prototype.getClassName()] = NodeClass;
|
||||
if (boundsCoverPaint)
|
||||
_boundedShapes.add(NodeClass);
|
||||
};
|
||||
Konva._injectGlobal(Konva);
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import type { ContainerConfig } from './Container.ts';
|
||||
import { Container } from './Container.ts';
|
||||
import type { Node } from './Node.ts';
|
||||
import type { Shape } from './Shape.ts';
|
||||
import type { SceneCanvas } from './Canvas.ts';
|
||||
import type { GetSet } from './types.ts';
|
||||
export interface GroupConfig extends ContainerConfig {
|
||||
isolated?: boolean;
|
||||
}
|
||||
/**
|
||||
* Group constructor. Groups are used to contain shapes or other groups.
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Container
|
||||
* @param {Object} config
|
||||
* @param {Boolean} [config.isolated=false] composite children as one live image
|
||||
* @@nodeParams
|
||||
* @@containerParams
|
||||
* @example
|
||||
* var group = new Konva.Group();
|
||||
*/
|
||||
export declare class Group extends Container<Group | Shape, GroupConfig> {
|
||||
_validateAdd(child: Node): void;
|
||||
_drawChildNodes(drawMethod: any, canvas: SceneCanvas, top?: Node): void;
|
||||
isolated: GetSet<boolean, this>;
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
import { Util } from "./Util.js";
|
||||
import { Container } from "./Container.js";
|
||||
import { _registerNode } from "./Global.js";
|
||||
import { Factory } from "./Factory.js";
|
||||
import { getBooleanValidator } from "./Validators.js";
|
||||
/**
|
||||
* Group constructor. Groups are used to contain shapes or other groups.
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Container
|
||||
* @param {Object} config
|
||||
* @param {Boolean} [config.isolated=false] composite children as one live image
|
||||
* @@nodeParams
|
||||
* @@containerParams
|
||||
* @example
|
||||
* var group = new Konva.Group();
|
||||
*/
|
||||
export class Group extends Container {
|
||||
_validateAdd(child) {
|
||||
const type = child.getType();
|
||||
if (type !== 'Group' && type !== 'Shape') {
|
||||
Util.throw('You may only add groups and shapes to groups.');
|
||||
}
|
||||
}
|
||||
_drawChildNodes(drawMethod, canvas, top) {
|
||||
// cache() already provides an isolated surface and owns its snapshot.
|
||||
if (drawMethod !== 'drawScene' || !this.isolated() || top === this) {
|
||||
return super._drawChildNodes(drawMethod, canvas, top);
|
||||
}
|
||||
if (!canvas.width || !canvas.height) {
|
||||
return;
|
||||
}
|
||||
const context = canvas.getContext();
|
||||
const surface = canvas._prepareIsolationCanvas(this.getClientRect({ _forDrawing: true, relativeTo: top }));
|
||||
const surfaceContext = surface.getContext();
|
||||
surfaceContext._opacityRoot = this;
|
||||
context.save();
|
||||
try {
|
||||
super._drawChildNodes(drawMethod, surface, top);
|
||||
context._applyOpacity(this);
|
||||
context._drawDeviceBuffer(surface);
|
||||
}
|
||||
catch (error) {
|
||||
// A throwing sceneFunc can leave unbalanced saves or a clip on the
|
||||
// scratch context. Discard it rather than contaminate the next draw.
|
||||
canvas._releaseIsolationCanvas();
|
||||
throw error;
|
||||
}
|
||||
finally {
|
||||
context.restore();
|
||||
surfaceContext._opacityRoot = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get or set group isolation. Default is false.
|
||||
* An isolated group draws its children into a transparent canvas on each scene draw.
|
||||
* The group opacity and globalCompositeOperation apply once to the completed image.
|
||||
* Child blend modes and erasing affect only the content inside that canvas.
|
||||
*
|
||||
* Explicit cache() calls keep their snapshot behavior. clearCache() resumes live drawing.
|
||||
* Hit testing keeps its normal behavior, including hits on erased content.
|
||||
* For video or canvas sources, schedule draws when the source changes.
|
||||
*
|
||||
* Buffers use conservative content bounds at the destination pixel ratio.
|
||||
* Moving the buffer origin changes native edge coverage. A few edge pixels
|
||||
* can differ substantially; isolation does not promise pixel-identical antialiasing.
|
||||
* Like cache(), isolation requires custom drawing (sceneFunc, charRenderFunc) to provide accurate
|
||||
* bounds, including any pixels it paints outside its size: selfRectFunc or a
|
||||
* getSelfRect() override.
|
||||
* Text is bounded by its lines plus one font size, so a glyph with many stacked
|
||||
* marks can be clipped. node-canvas shadows can change with bitmap origin.
|
||||
* Siblings reuse a buffer; unused or oversized buffers are freed after each layer draw.
|
||||
* @name Konva.Group#isolated
|
||||
* @method
|
||||
* @param {Boolean} isolated
|
||||
* @returns {Boolean}
|
||||
* @example
|
||||
* const group = new Konva.Group({ isolated: true, opacity: 0.5 });
|
||||
* group.add(shape1, shape2);
|
||||
* group.isolated(false);
|
||||
*/
|
||||
Factory.addGetterSetter(Group, 'isolated', false, getBooleanValidator());
|
||||
Group.prototype.nodeType = 'Group';
|
||||
_registerNode(Group);
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
import type { ContainerConfig } from './Container.ts';
|
||||
import { Container } from './Container.ts';
|
||||
import { Node } from './Node.ts';
|
||||
import { SceneCanvas, HitCanvas } from './Canvas.ts';
|
||||
import type { Stage } from './Stage.ts';
|
||||
import type { GetSet, IRect, Vector2d } from './types.ts';
|
||||
import type { Group } from './Group.ts';
|
||||
import type { Shape } from './Shape.ts';
|
||||
export interface LayerConfig extends ContainerConfig {
|
||||
clearBeforeDraw?: boolean;
|
||||
hitGraphEnabled?: boolean;
|
||||
imageSmoothingEnabled?: boolean;
|
||||
}
|
||||
/**
|
||||
* Layer constructor. Layers are tied to their own canvas element and are used
|
||||
* to contain groups or shapes.
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Container
|
||||
* @param {Object} config
|
||||
* @param {Boolean} [config.clearBeforeDraw] set this property to false if you don't want
|
||||
* to clear the canvas before each layer draw. The default value is true.
|
||||
* @@nodeParams
|
||||
* @@containerParams
|
||||
* @example
|
||||
* var layer = new Konva.Layer();
|
||||
* stage.add(layer);
|
||||
* // now you can add shapes, groups into the layer
|
||||
*/
|
||||
export declare class Layer extends Container<Group | Shape> {
|
||||
canvas: SceneCanvas;
|
||||
hitCanvas: HitCanvas;
|
||||
_waitingForDraw: boolean;
|
||||
constructor(config?: LayerConfig);
|
||||
createPNGStream(): any;
|
||||
/**
|
||||
* get layer canvas wrapper
|
||||
* @method
|
||||
* @name Konva.Layer#getCanvas
|
||||
*/
|
||||
getCanvas(): SceneCanvas;
|
||||
/**
|
||||
* get native canvas element
|
||||
* @method
|
||||
* @name Konva.Layer#getNativeCanvasElement
|
||||
*/
|
||||
getNativeCanvasElement(): HTMLCanvasElement;
|
||||
/**
|
||||
* get layer hit canvas
|
||||
* @method
|
||||
* @name Konva.Layer#getHitCanvas
|
||||
*/
|
||||
getHitCanvas(): HitCanvas;
|
||||
/**
|
||||
* get layer canvas context
|
||||
* @method
|
||||
* @name Konva.Layer#getContext
|
||||
*/
|
||||
getContext(): import("./Context.ts").Context;
|
||||
/**
|
||||
* clear the scene and hit canvas of the layer. The nodes stay and are
|
||||
* drawn again by the next `draw()`
|
||||
* @method
|
||||
* @name Konva.Layer#clear
|
||||
* @param {Object} [bounds] clear only this rectangle: `{ x, y, width, height }`
|
||||
* @returns {Konva.Layer}
|
||||
* @example
|
||||
* layer.clear();
|
||||
* layer.clear({ x: 0, y: 0, width: 100, height: 100 });
|
||||
*/
|
||||
clear(bounds?: IRect): this;
|
||||
setZIndex(index: number): this;
|
||||
moveToTop(): boolean;
|
||||
moveUp(): boolean;
|
||||
moveDown(): boolean;
|
||||
moveToBottom(): boolean;
|
||||
getLayer(): this;
|
||||
remove(): this;
|
||||
getStage(): Stage;
|
||||
_setSize({ width, height }: {
|
||||
width: any;
|
||||
height: any;
|
||||
}): this;
|
||||
_syncHitCanvasSize(): void;
|
||||
_validateAdd(child: any): void;
|
||||
_toKonvaCanvas(config: any): SceneCanvas;
|
||||
_checkVisibility(): void;
|
||||
_setSmoothEnabled(): void;
|
||||
/**
|
||||
* get/set width of layer. getter return width of stage. setter doing nothing.
|
||||
* if you want change width use `stage.width(value);`
|
||||
* @name Konva.Layer#width
|
||||
* @method
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* var width = layer.width();
|
||||
*/
|
||||
getWidth(): number | undefined;
|
||||
setWidth(): void;
|
||||
/**
|
||||
* get/set height of layer.getter return height of stage. setter doing nothing.
|
||||
* if you want change height use `stage.height(value);`
|
||||
* @name Konva.Layer#height
|
||||
* @method
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* var height = layer.height();
|
||||
*/
|
||||
getHeight(): number | undefined;
|
||||
setHeight(): void;
|
||||
/**
|
||||
* batch draw. this function will not do immediate draw
|
||||
* but it will schedule drawing to next tick (requestAnimFrame)
|
||||
* @method
|
||||
* @name Konva.Layer#batchDraw
|
||||
* @return {Konva.Layer} this
|
||||
*/
|
||||
batchDraw(): this;
|
||||
/**
|
||||
* get visible intersection shape. This is the preferred
|
||||
* method for determining if a point intersects a shape or not.
|
||||
* It reads the hit canvas, so it finds what pointer events would: nodes with
|
||||
* listening set to false or invisible nodes are not detected, a shape with opacity 0
|
||||
* is, and `hitStrokeWidth` counts. The position is relative to the top left corner of the
|
||||
* stage container, like `stage.getPointerPosition()`, without the stage transform
|
||||
* @method
|
||||
* @name Konva.Layer#getIntersection
|
||||
* @param {Object} pos
|
||||
* @param {Number} pos.x
|
||||
* @param {Number} pos.y
|
||||
* @returns {Konva.Node}
|
||||
* @example
|
||||
* var shape = layer.getIntersection({x: 50, y: 50});
|
||||
*/
|
||||
getIntersection(pos: Vector2d): Shape<import("./Shape.ts").ShapeConfig> | null;
|
||||
_getIntersection(pos: Vector2d): {
|
||||
shape?: Shape;
|
||||
antialiased?: boolean;
|
||||
};
|
||||
drawScene(can?: SceneCanvas, top?: Node): this;
|
||||
shouldDrawHit(top?: Node): boolean;
|
||||
drawHit(can?: HitCanvas, top?: Node): this;
|
||||
/**
|
||||
* enable hit graph. **DEPRECATED!** Use `layer.listening(true)` instead.
|
||||
* @name Konva.Layer#enableHitGraph
|
||||
* @method
|
||||
* @returns {Layer}
|
||||
*/
|
||||
enableHitGraph(): this;
|
||||
/**
|
||||
* disable hit graph. **DEPRECATED!** Use `layer.listening(false)` instead.
|
||||
* @name Konva.Layer#disableHitGraph
|
||||
* @method
|
||||
* @returns {Layer}
|
||||
*/
|
||||
disableHitGraph(): this;
|
||||
setHitGraphEnabled(val: any): void;
|
||||
getHitGraphEnabled(val: any): boolean;
|
||||
/**
|
||||
* Show or hide hit canvas over the stage. May be useful for debugging custom hitFunc
|
||||
* @name Konva.Layer#toggleHitCanvas
|
||||
* @method
|
||||
*/
|
||||
toggleHitCanvas(): void;
|
||||
destroy(): this;
|
||||
hitGraphEnabled: GetSet<boolean, this>;
|
||||
clearBeforeDraw: GetSet<boolean, this>;
|
||||
imageSmoothingEnabled: GetSet<boolean, this>;
|
||||
}
|
||||
/**
|
||||
* get/set hitGraphEnabled flag. **DEPRECATED!** Use `layer.listening(false)` instead.
|
||||
* Disabling the hit graph will greatly increase
|
||||
* draw performance because the hit graph will not be redrawn each time the layer is
|
||||
* drawn. This, however, also disables mouse/touch event detection
|
||||
* @name Konva.Layer#hitGraphEnabled
|
||||
* @method
|
||||
* @param {Boolean} enabled
|
||||
* @returns {Boolean}
|
||||
* @example
|
||||
* // get hitGraphEnabled flag
|
||||
* var hitGraphEnabled = layer.hitGraphEnabled();
|
||||
*
|
||||
* // disable hit graph
|
||||
* layer.hitGraphEnabled(false);
|
||||
*
|
||||
* // enable hit graph
|
||||
* layer.hitGraphEnabled(true);
|
||||
*/
|
||||
+532
@@ -0,0 +1,532 @@
|
||||
import { Util } from "./Util.js";
|
||||
import { Container } from "./Container.js";
|
||||
import { Node } from "./Node.js";
|
||||
import { Factory } from "./Factory.js";
|
||||
import { SceneCanvas, HitCanvas } from "./Canvas.js";
|
||||
import { shapes } from "./Shape.js";
|
||||
import { _registerNode, Konva } from "./Global.js";
|
||||
import { DD } from "./DragAndDrop.js";
|
||||
// constants
|
||||
const BEFORE_DRAW = 'beforeDraw', DRAW = 'draw',
|
||||
// getIntersection() reads the hit canvas around an edge pixel in blocks
|
||||
// of this radius, doubling it as long as the search goes on
|
||||
HIT_SEARCH_RADIUS = 10, HIT_SEARCH_MAX_DISTANCE = 256;
|
||||
// the shape drawn at a pixel of the hit graph: an opaque pixel, or an edge
|
||||
// pixel that the shape covers by at least half, so that its colour read back
|
||||
// premultiplied still rounds to the key of the shape (see getHitColorKey)
|
||||
function getHitShape(data, i) {
|
||||
return data[i + 3] >= 128
|
||||
? shapes[Util.getHitColorKey(data[i], data[i + 1], data[i + 2])]
|
||||
: undefined;
|
||||
}
|
||||
/**
|
||||
* Layer constructor. Layers are tied to their own canvas element and are used
|
||||
* to contain groups or shapes.
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Container
|
||||
* @param {Object} config
|
||||
* @param {Boolean} [config.clearBeforeDraw] set this property to false if you don't want
|
||||
* to clear the canvas before each layer draw. The default value is true.
|
||||
* @@nodeParams
|
||||
* @@containerParams
|
||||
* @example
|
||||
* var layer = new Konva.Layer();
|
||||
* stage.add(layer);
|
||||
* // now you can add shapes, groups into the layer
|
||||
*/
|
||||
export class Layer extends Container {
|
||||
constructor(config) {
|
||||
super(config);
|
||||
this.canvas = new SceneCanvas();
|
||||
this.hitCanvas = new HitCanvas({
|
||||
pixelRatio: 1,
|
||||
});
|
||||
this._waitingForDraw = false;
|
||||
this.on('visibleChange.konva', this._checkVisibility);
|
||||
this._checkVisibility();
|
||||
this.on('imageSmoothingEnabledChange.konva', this._setSmoothEnabled);
|
||||
this._setSmoothEnabled();
|
||||
}
|
||||
// for nodejs?
|
||||
createPNGStream() {
|
||||
const c = this.canvas._canvas;
|
||||
return c.createPNGStream();
|
||||
}
|
||||
/**
|
||||
* get layer canvas wrapper
|
||||
* @method
|
||||
* @name Konva.Layer#getCanvas
|
||||
*/
|
||||
getCanvas() {
|
||||
return this.canvas;
|
||||
}
|
||||
/**
|
||||
* get native canvas element
|
||||
* @method
|
||||
* @name Konva.Layer#getNativeCanvasElement
|
||||
*/
|
||||
getNativeCanvasElement() {
|
||||
return this.canvas._canvas;
|
||||
}
|
||||
/**
|
||||
* get layer hit canvas
|
||||
* @method
|
||||
* @name Konva.Layer#getHitCanvas
|
||||
*/
|
||||
getHitCanvas() {
|
||||
return this.hitCanvas;
|
||||
}
|
||||
/**
|
||||
* get layer canvas context
|
||||
* @method
|
||||
* @name Konva.Layer#getContext
|
||||
*/
|
||||
getContext() {
|
||||
return this.getCanvas().getContext();
|
||||
}
|
||||
/**
|
||||
* clear the scene and hit canvas of the layer. The nodes stay and are
|
||||
* drawn again by the next `draw()`
|
||||
* @method
|
||||
* @name Konva.Layer#clear
|
||||
* @param {Object} [bounds] clear only this rectangle: `{ x, y, width, height }`
|
||||
* @returns {Konva.Layer}
|
||||
* @example
|
||||
* layer.clear();
|
||||
* layer.clear({ x: 0, y: 0, width: 100, height: 100 });
|
||||
*/
|
||||
clear(bounds) {
|
||||
this.getContext().clear(bounds);
|
||||
this.getHitCanvas().getContext().clear(bounds);
|
||||
return this;
|
||||
}
|
||||
// extend Node.prototype.setZIndex
|
||||
setZIndex(index) {
|
||||
super.setZIndex(index);
|
||||
const stage = this.getStage();
|
||||
if (stage && stage.content) {
|
||||
stage.content.removeChild(this.getNativeCanvasElement());
|
||||
if (index < stage.children.length - 1) {
|
||||
stage.content.insertBefore(this.getNativeCanvasElement(), stage.children[index + 1].getCanvas()._canvas);
|
||||
}
|
||||
else {
|
||||
stage.content.appendChild(this.getNativeCanvasElement());
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
moveToTop() {
|
||||
Node.prototype.moveToTop.call(this);
|
||||
const stage = this.getStage();
|
||||
if (stage && stage.content) {
|
||||
stage.content.removeChild(this.getNativeCanvasElement());
|
||||
stage.content.appendChild(this.getNativeCanvasElement());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
moveUp() {
|
||||
const moved = Node.prototype.moveUp.call(this);
|
||||
if (!moved) {
|
||||
return false;
|
||||
}
|
||||
const stage = this.getStage();
|
||||
if (!stage || !stage.content) {
|
||||
return false;
|
||||
}
|
||||
stage.content.removeChild(this.getNativeCanvasElement());
|
||||
if (this.index < stage.children.length - 1) {
|
||||
stage.content.insertBefore(this.getNativeCanvasElement(), stage.children[this.index + 1].getCanvas()._canvas);
|
||||
}
|
||||
else {
|
||||
stage.content.appendChild(this.getNativeCanvasElement());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// extend Node.prototype.moveDown
|
||||
moveDown() {
|
||||
if (Node.prototype.moveDown.call(this)) {
|
||||
const stage = this.getStage();
|
||||
if (stage) {
|
||||
const children = stage.children;
|
||||
if (stage.content) {
|
||||
stage.content.removeChild(this.getNativeCanvasElement());
|
||||
stage.content.insertBefore(this.getNativeCanvasElement(), children[this.index + 1].getCanvas()._canvas);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// extend Node.prototype.moveToBottom
|
||||
moveToBottom() {
|
||||
if (Node.prototype.moveToBottom.call(this)) {
|
||||
const stage = this.getStage();
|
||||
if (stage) {
|
||||
const children = stage.children;
|
||||
if (stage.content) {
|
||||
stage.content.removeChild(this.getNativeCanvasElement());
|
||||
stage.content.insertBefore(this.getNativeCanvasElement(), children[1].getCanvas()._canvas);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
getLayer() {
|
||||
return this;
|
||||
}
|
||||
remove() {
|
||||
var _a, _b;
|
||||
super.remove();
|
||||
// both canvases live in stage.content: the scene one always, the hit one
|
||||
// while toggleHitCanvas() shows it
|
||||
const scene = this.getNativeCanvasElement();
|
||||
const hit = this.getHitCanvas()._canvas;
|
||||
(_a = scene.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(scene);
|
||||
(_b = hit.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(hit);
|
||||
return this;
|
||||
}
|
||||
getStage() {
|
||||
return this.parent;
|
||||
}
|
||||
// the canvases follow the stage size. The public size() of a layer, like
|
||||
// width() and height(), warns and does nothing
|
||||
_setSize({ width, height }) {
|
||||
this.canvas.setSize(width, height);
|
||||
this._syncHitCanvasSize();
|
||||
this._setSmoothEnabled();
|
||||
return this;
|
||||
}
|
||||
// the hit canvas is only allocated while the layer is listening;
|
||||
// for a non-listening layer it is kept released (0x0), so a purely
|
||||
// presentational layer does not pay for a stage-sized hit bitmap
|
||||
// https://github.com/konvajs/konva/issues/2009
|
||||
_syncHitCanvasSize() {
|
||||
const listening = this.isListening();
|
||||
this.hitCanvas.setSizeIfChanged((listening ? this.getWidth() : 0) || 0, (listening ? this.getHeight() : 0) || 0);
|
||||
}
|
||||
_validateAdd(child) {
|
||||
const type = child.getType();
|
||||
if (type !== 'Group' && type !== 'Shape') {
|
||||
Util.throw('You may only add groups and shapes to a layer.');
|
||||
}
|
||||
}
|
||||
_toKonvaCanvas(config) {
|
||||
config = { ...config };
|
||||
config.width = config.width || this.getWidth();
|
||||
config.height = config.height || this.getHeight();
|
||||
config.x = config.x !== undefined ? config.x : this.x();
|
||||
config.y = config.y !== undefined ? config.y : this.y();
|
||||
return Node.prototype._toKonvaCanvas.call(this, config);
|
||||
}
|
||||
_checkVisibility() {
|
||||
const visible = this.visible();
|
||||
if (visible) {
|
||||
this.canvas._canvas.style.display = 'block';
|
||||
}
|
||||
else {
|
||||
this.canvas._canvas.style.display = 'none';
|
||||
}
|
||||
}
|
||||
_setSmoothEnabled() {
|
||||
this.getContext()._context.imageSmoothingEnabled =
|
||||
this.imageSmoothingEnabled();
|
||||
}
|
||||
/**
|
||||
* get/set width of layer. getter return width of stage. setter doing nothing.
|
||||
* if you want change width use `stage.width(value);`
|
||||
* @name Konva.Layer#width
|
||||
* @method
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* var width = layer.width();
|
||||
*/
|
||||
getWidth() {
|
||||
if (this.parent) {
|
||||
return this.parent.width();
|
||||
}
|
||||
}
|
||||
setWidth() {
|
||||
Util.warn('Can not change width of layer. Use "stage.width(value)" function instead.');
|
||||
}
|
||||
/**
|
||||
* get/set height of layer.getter return height of stage. setter doing nothing.
|
||||
* if you want change height use `stage.height(value);`
|
||||
* @name Konva.Layer#height
|
||||
* @method
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* var height = layer.height();
|
||||
*/
|
||||
getHeight() {
|
||||
if (this.parent) {
|
||||
return this.parent.height();
|
||||
}
|
||||
}
|
||||
setHeight() {
|
||||
Util.warn('Can not change height of layer. Use "stage.height(value)" function instead.');
|
||||
}
|
||||
/**
|
||||
* batch draw. this function will not do immediate draw
|
||||
* but it will schedule drawing to next tick (requestAnimFrame)
|
||||
* @method
|
||||
* @name Konva.Layer#batchDraw
|
||||
* @return {Konva.Layer} this
|
||||
*/
|
||||
batchDraw() {
|
||||
var _a;
|
||||
if (!this._waitingForDraw) {
|
||||
this._waitingForDraw = true;
|
||||
// ask the window the stage is rendered in for the frame: a stage in a
|
||||
// popout window keeps drawing while the opener window is not visible
|
||||
Util.requestAnimFrame(() => {
|
||||
// reset before drawing, so a throwing draw does not freeze the layer
|
||||
// and a draw handler can request the next frame
|
||||
this._waitingForDraw = false;
|
||||
this.draw();
|
||||
}, (_a = this.getStage()) === null || _a === void 0 ? void 0 : _a._getOwnerWindow());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* get visible intersection shape. This is the preferred
|
||||
* method for determining if a point intersects a shape or not.
|
||||
* It reads the hit canvas, so it finds what pointer events would: nodes with
|
||||
* listening set to false or invisible nodes are not detected, a shape with opacity 0
|
||||
* is, and `hitStrokeWidth` counts. The position is relative to the top left corner of the
|
||||
* stage container, like `stage.getPointerPosition()`, without the stage transform
|
||||
* @method
|
||||
* @name Konva.Layer#getIntersection
|
||||
* @param {Object} pos
|
||||
* @param {Number} pos.x
|
||||
* @param {Number} pos.y
|
||||
* @returns {Konva.Node}
|
||||
* @example
|
||||
* var shape = layer.getIntersection({x: 50, y: 50});
|
||||
*/
|
||||
getIntersection(pos) {
|
||||
if (!this.isListening() || !this.isVisible()) {
|
||||
return null;
|
||||
}
|
||||
const hit = this._getIntersection(pos);
|
||||
if (hit.shape) {
|
||||
return hit.shape;
|
||||
}
|
||||
if (!hit.antialiased) {
|
||||
return null;
|
||||
}
|
||||
// the pointer is on the outer part of an edge, or on an opaque pixel with
|
||||
// the colours of two shapes mixed. Walk outward ring by ring to the
|
||||
// nearest pixel that resolves to a shape. The smoothed edge of a cached
|
||||
// node that is scaled up can be many pixels wide, so keep going while the
|
||||
// alpha still rises (the ramp leads into the shape) or the ring holds
|
||||
// mixed pixels. A thin line has a flat alpha, so the search stops at once
|
||||
// instead of walking along it to an unrelated shape nearby
|
||||
const ratio = this.hitCanvas.pixelRatio, cx = Math.floor(pos.x * ratio), cy = Math.floor(pos.y * ratio);
|
||||
const read = (r) => this.hitCanvas.context.getImageData(cx - r, cy - r, r * 2 + 1, r * 2 + 1)
|
||||
.data;
|
||||
let r = HIT_SEARCH_RADIUS, size = r * 2 + 1, data = read(r), previousMax = data[(r * size + r) * 4 + 3];
|
||||
for (let d = 1; d <= HIT_SEARCH_MAX_DISTANCE; d++) {
|
||||
if (d > r) {
|
||||
r = Math.min(HIT_SEARCH_MAX_DISTANCE, r * 2);
|
||||
size = r * 2 + 1;
|
||||
data = read(r);
|
||||
}
|
||||
let max = 0;
|
||||
for (let y = -d; y <= d; y++) {
|
||||
// whole top and bottom rows, only the two side pixels of the others
|
||||
const step = Math.abs(y) === d ? 1 : d * 2;
|
||||
for (let x = -d; x <= d; x += step) {
|
||||
const i = ((y + r) * size + x + r) * 4;
|
||||
const shape = getHitShape(data, i);
|
||||
if (shape) {
|
||||
return shape;
|
||||
}
|
||||
max = Math.max(max, data[i + 3]);
|
||||
}
|
||||
}
|
||||
// 255 here is a mixed pixel, as an opaque one resolved above
|
||||
if (max <= previousMax && max !== 255) {
|
||||
return null;
|
||||
}
|
||||
previousMax = max;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
_getIntersection(pos) {
|
||||
// a non-listening layer keeps its hit canvas released (0x0),
|
||||
// and a just-enabled layer may not have drawn its hit graph yet
|
||||
if (!this.hitCanvas.width || !this.hitCanvas.height) {
|
||||
return {};
|
||||
}
|
||||
const ratio = this.hitCanvas.pixelRatio;
|
||||
const p = this.hitCanvas.context.getImageData(Math.floor(pos.x * ratio), Math.floor(pos.y * ratio), 1, 1).data;
|
||||
const shape = getHitShape(p, 0);
|
||||
if (shape) {
|
||||
return { shape };
|
||||
}
|
||||
if (p[3] > 0) {
|
||||
// an edge pixel, or an opaque one with the colours of two shapes mixed
|
||||
return { antialiased: true };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
drawScene(can, top) {
|
||||
const layer = this.getLayer(), canvas = can || (layer && layer.getCanvas());
|
||||
this._fire(BEFORE_DRAW, {
|
||||
node: this,
|
||||
});
|
||||
if (this.clearBeforeDraw()) {
|
||||
canvas.getContext().clear();
|
||||
}
|
||||
Container.prototype.drawScene.call(this, canvas, top);
|
||||
canvas._trimIsolationCanvas();
|
||||
this._fire(DRAW, {
|
||||
node: this,
|
||||
});
|
||||
return this;
|
||||
}
|
||||
// the hit graph is skipped while a node of the layer (or the stage) is
|
||||
// dragged or a transformer on the layer, or of a node on it, is
|
||||
// transforming, as the layer is redrawn every frame; the draw that follows
|
||||
// restores it
|
||||
shouldDrawHit(top) {
|
||||
var _a;
|
||||
if (!super.shouldDrawHit(top)) {
|
||||
return false;
|
||||
}
|
||||
if (top || Konva.hitOnDragEnabled) {
|
||||
return true;
|
||||
}
|
||||
let underDrag = false;
|
||||
DD._dragElements.forEach((elem) => {
|
||||
if (elem.dragStatus === 'dragging' &&
|
||||
(elem.node === this.getStage() || elem.node.getLayer() === this)) {
|
||||
underDrag = true;
|
||||
}
|
||||
});
|
||||
return !underDrag && !((_a = Konva['Transformer']) === null || _a === void 0 ? void 0 : _a._isLayerTransforming(this));
|
||||
}
|
||||
drawHit(can, top) {
|
||||
const layer = this.getLayer(), canvas = can || (layer && layer.hitCanvas);
|
||||
if (!can && layer) {
|
||||
// allocate or release the hit canvas to match the current listening
|
||||
// state - this also picks up a listening change made after the last draw
|
||||
layer._syncHitCanvasSize();
|
||||
if (layer.clearBeforeDraw()) {
|
||||
layer.getHitCanvas().getContext().clear();
|
||||
}
|
||||
}
|
||||
Container.prototype.drawHit.call(this, canvas, top);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* enable hit graph. **DEPRECATED!** Use `layer.listening(true)` instead.
|
||||
* @name Konva.Layer#enableHitGraph
|
||||
* @method
|
||||
* @returns {Layer}
|
||||
*/
|
||||
enableHitGraph() {
|
||||
this.hitGraphEnabled(true);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* disable hit graph. **DEPRECATED!** Use `layer.listening(false)` instead.
|
||||
* @name Konva.Layer#disableHitGraph
|
||||
* @method
|
||||
* @returns {Layer}
|
||||
*/
|
||||
disableHitGraph() {
|
||||
this.hitGraphEnabled(false);
|
||||
return this;
|
||||
}
|
||||
setHitGraphEnabled(val) {
|
||||
Util.warn('hitGraphEnabled method is deprecated. Please use layer.listening() instead.');
|
||||
this.listening(val);
|
||||
}
|
||||
getHitGraphEnabled(val) {
|
||||
Util.warn('hitGraphEnabled method is deprecated. Please use layer.listening() instead.');
|
||||
return this.listening();
|
||||
}
|
||||
/**
|
||||
* Show or hide hit canvas over the stage. May be useful for debugging custom hitFunc
|
||||
* @name Konva.Layer#toggleHitCanvas
|
||||
* @method
|
||||
*/
|
||||
toggleHitCanvas() {
|
||||
if (!this.parent || !this.parent['content']) {
|
||||
return;
|
||||
}
|
||||
const parent = this.parent;
|
||||
const added = !!this.hitCanvas._canvas.parentNode;
|
||||
if (added) {
|
||||
parent.content.removeChild(this.hitCanvas._canvas);
|
||||
}
|
||||
else {
|
||||
parent.content.appendChild(this.hitCanvas._canvas);
|
||||
}
|
||||
}
|
||||
destroy() {
|
||||
this.getCanvas()._releaseIsolationCanvas();
|
||||
Util.releaseCanvas(this.getNativeCanvasElement(), this.getHitCanvas()._canvas);
|
||||
return super.destroy();
|
||||
}
|
||||
}
|
||||
Layer.prototype.nodeType = 'Layer';
|
||||
_registerNode(Layer);
|
||||
/**
|
||||
* get/set imageSmoothingEnabled flag
|
||||
* For more info see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingEnabled
|
||||
* @name Konva.Layer#imageSmoothingEnabled
|
||||
* @method
|
||||
* @param {Boolean} imageSmoothingEnabled
|
||||
* @returns {Boolean}
|
||||
* @example
|
||||
* // get imageSmoothingEnabled flag
|
||||
* var imageSmoothingEnabled = layer.imageSmoothingEnabled();
|
||||
*
|
||||
* layer.imageSmoothingEnabled(false);
|
||||
*
|
||||
* layer.imageSmoothingEnabled(true);
|
||||
*/
|
||||
Factory.addGetterSetter(Layer, 'imageSmoothingEnabled', true);
|
||||
/**
|
||||
* get/set clearBeforeDraw flag which determines if the layer is cleared or not
|
||||
* before drawing
|
||||
* @name Konva.Layer#clearBeforeDraw
|
||||
* @method
|
||||
* @param {Boolean} clearBeforeDraw
|
||||
* @returns {Boolean}
|
||||
* @example
|
||||
* // get clearBeforeDraw flag
|
||||
* var clearBeforeDraw = layer.clearBeforeDraw();
|
||||
*
|
||||
* // disable clear before draw
|
||||
* layer.clearBeforeDraw(false);
|
||||
*
|
||||
* // enable clear before draw
|
||||
* layer.clearBeforeDraw(true);
|
||||
*/
|
||||
Factory.addGetterSetter(Layer, 'clearBeforeDraw', true);
|
||||
// the getter and setter are the deprecated ones above
|
||||
Factory.addOverloadedGetterSetter(Layer, 'hitGraphEnabled');
|
||||
/**
|
||||
* get/set hitGraphEnabled flag. **DEPRECATED!** Use `layer.listening(false)` instead.
|
||||
* Disabling the hit graph will greatly increase
|
||||
* draw performance because the hit graph will not be redrawn each time the layer is
|
||||
* drawn. This, however, also disables mouse/touch event detection
|
||||
* @name Konva.Layer#hitGraphEnabled
|
||||
* @method
|
||||
* @param {Boolean} enabled
|
||||
* @returns {Boolean}
|
||||
* @example
|
||||
* // get hitGraphEnabled flag
|
||||
* var hitGraphEnabled = layer.hitGraphEnabled();
|
||||
*
|
||||
* // disable hit graph
|
||||
* layer.hitGraphEnabled(false);
|
||||
*
|
||||
* // enable hit graph
|
||||
* layer.hitGraphEnabled(true);
|
||||
*/
|
||||
+1017
File diff suppressed because it is too large
Load Diff
+3004
File diff suppressed because it is too large
Load Diff
+11
@@ -0,0 +1,11 @@
|
||||
import type { KonvaEventObject, Node } from './Node.ts';
|
||||
import type { Shape } from './Shape.ts';
|
||||
import type { Stage } from './Stage.ts';
|
||||
export interface KonvaPointerEvent extends KonvaEventObject<PointerEvent> {
|
||||
pointerId: number;
|
||||
}
|
||||
export declare function getCapturedShape(pointerId: number, stage?: Stage): Shape<import("./Shape.ts").ShapeConfig> | Stage | undefined;
|
||||
export declare function hasPointerCapture(pointerId: number, shape: Shape | Stage): boolean;
|
||||
export declare function setPointerCapture(pointerId: number, shape: Shape | Stage): void;
|
||||
export declare function releaseCapturesOf(node: Node): void;
|
||||
export declare function releaseCapture(pointerId: number, target?: Shape | Stage): void;
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
import { Konva } from "./Global.js";
|
||||
const Captures = new Map();
|
||||
// we may use this module for capturing touch events too
|
||||
// so make sure we don't do something super specific to pointer
|
||||
const SUPPORT_POINTER_EVENTS = Konva._global['PointerEvent'] !== undefined;
|
||||
export function getCapturedShape(pointerId, stage) {
|
||||
const capture = Captures.get(pointerId);
|
||||
return capture && (!stage || capture.stage === stage)
|
||||
? capture.shape
|
||||
: undefined;
|
||||
}
|
||||
function fireCapture(shape, type, pointerId) {
|
||||
shape._fire(type, { evt: new PointerEvent(type, { pointerId }), pointerId });
|
||||
}
|
||||
export function hasPointerCapture(pointerId, shape) {
|
||||
var _a;
|
||||
return ((_a = Captures.get(pointerId)) === null || _a === void 0 ? void 0 : _a.shape) === shape;
|
||||
}
|
||||
export function setPointerCapture(pointerId, shape) {
|
||||
var _a;
|
||||
releaseCapture(pointerId);
|
||||
const stage = shape.getStage();
|
||||
if (!stage)
|
||||
return;
|
||||
// Native capture belongs to this stage even if the shape is reparented.
|
||||
Captures.set(pointerId, { shape, stage });
|
||||
if (SUPPORT_POINTER_EVENTS) {
|
||||
// capture on the DOM level too, so the stage keeps receiving the events
|
||||
// of that pointer even when it moves outside of the stage container
|
||||
// https://github.com/konvajs/konva/issues/1992
|
||||
try {
|
||||
(_a = stage.content) === null || _a === void 0 ? void 0 : _a.setPointerCapture(pointerId);
|
||||
}
|
||||
catch (e) {
|
||||
// capture is possible only for an active pointer;
|
||||
// ids of mouse and touch events (999 and touch identifiers) and
|
||||
// programmatic calls outside of a pointer event land here
|
||||
}
|
||||
fireCapture(shape, 'gotpointercapture', pointerId);
|
||||
}
|
||||
}
|
||||
// Destroying either the captured node or its native stage ends capture.
|
||||
export function releaseCapturesOf(node) {
|
||||
Captures.forEach(({ shape, stage }, pointerId) => {
|
||||
if (shape === node || stage === node) {
|
||||
releaseCapture(pointerId);
|
||||
}
|
||||
});
|
||||
}
|
||||
export function releaseCapture(pointerId, target) {
|
||||
var _a;
|
||||
const capture = Captures.get(pointerId);
|
||||
// a node can only release its own capture
|
||||
if (!capture || (target && capture.shape !== target))
|
||||
return;
|
||||
const { shape, stage } = capture;
|
||||
Captures.delete(pointerId);
|
||||
if (SUPPORT_POINTER_EVENTS) {
|
||||
try {
|
||||
(_a = stage.content) === null || _a === void 0 ? void 0 : _a.releasePointerCapture(pointerId);
|
||||
}
|
||||
catch (e) {
|
||||
// same as in setPointerCapture: the pointer may not be active
|
||||
}
|
||||
fireCapture(shape, 'lostpointercapture', pointerId);
|
||||
}
|
||||
}
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
import type { GetClientRectConfig, NodeConfig } from './Node.ts';
|
||||
import { Node } from './Node.ts';
|
||||
import type { Context } from './Context.ts';
|
||||
import type { GetSet, Vector2d, IRect } from './types.ts';
|
||||
import type { HitCanvas, SceneCanvas } from './Canvas.ts';
|
||||
export type ShapeConfigHandler<TTarget> = {
|
||||
bivarianceHack(ctx: Context, shape: TTarget): void;
|
||||
}['bivarianceHack'];
|
||||
export type LineJoin = 'round' | 'bevel' | 'miter';
|
||||
export type LineCap = 'butt' | 'round' | 'square';
|
||||
export type ShapeConfig = NodeConfig & {
|
||||
fill?: string | CanvasGradient;
|
||||
fillPatternImage?: HTMLImageElement;
|
||||
fillPatternX?: number;
|
||||
fillPatternY?: number;
|
||||
fillPatternOffset?: Vector2d;
|
||||
fillPatternOffsetX?: number;
|
||||
fillPatternOffsetY?: number;
|
||||
fillPatternScale?: Vector2d;
|
||||
fillPatternScaleX?: number;
|
||||
fillPatternScaleY?: number;
|
||||
fillPatternRotation?: number;
|
||||
fillPatternRepeat?: string;
|
||||
fillLinearGradientStartPoint?: Vector2d;
|
||||
fillLinearGradientStartPointX?: number;
|
||||
fillLinearGradientStartPointY?: number;
|
||||
fillLinearGradientEndPoint?: Vector2d;
|
||||
fillLinearGradientEndPointX?: number;
|
||||
fillLinearGradientEndPointY?: number;
|
||||
fillLinearGradientColorStops?: Array<number | string>;
|
||||
strokeLinearGradientStartPoint?: Vector2d;
|
||||
strokeLinearGradientStartPointX?: number;
|
||||
strokeLinearGradientStartPointY?: number;
|
||||
strokeLinearGradientEndPoint?: Vector2d;
|
||||
strokeLinearGradientEndPointX?: number;
|
||||
strokeLinearGradientEndPointY?: number;
|
||||
strokeLinearGradientColorStops?: Array<number | string>;
|
||||
fillRadialGradientStartPoint?: Vector2d;
|
||||
fillRadialGradientStartPointX?: number;
|
||||
fillRadialGradientStartPointY?: number;
|
||||
fillRadialGradientEndPoint?: Vector2d;
|
||||
fillRadialGradientEndPointX?: number;
|
||||
fillRadialGradientEndPointY?: number;
|
||||
fillRadialGradientStartRadius?: number;
|
||||
fillRadialGradientEndRadius?: number;
|
||||
fillRadialGradientColorStops?: Array<number | string>;
|
||||
fillEnabled?: boolean;
|
||||
fillPriority?: string;
|
||||
fillRule?: CanvasFillRule;
|
||||
stroke?: string | CanvasGradient;
|
||||
strokeWidth?: number;
|
||||
fillAfterStrokeEnabled?: boolean;
|
||||
hitStrokeWidth?: number | string;
|
||||
strokeScaleEnabled?: boolean;
|
||||
strokeHitEnabled?: boolean;
|
||||
strokeEnabled?: boolean;
|
||||
lineJoin?: LineJoin;
|
||||
lineCap?: LineCap;
|
||||
miterLimit?: number;
|
||||
sceneFunc?: (con: Context, shape: Shape) => void;
|
||||
hitFunc?: (con: Context, shape: Shape) => void;
|
||||
selfRectFunc?: (shape: Shape) => IRect;
|
||||
shadowColor?: string;
|
||||
shadowBlur?: number;
|
||||
shadowOffset?: Vector2d;
|
||||
shadowOffsetX?: number;
|
||||
shadowOffsetY?: number;
|
||||
shadowOpacity?: number;
|
||||
shadowEnabled?: boolean;
|
||||
shadowForStrokeEnabled?: boolean;
|
||||
dash?: number[];
|
||||
dashOffset?: number;
|
||||
dashEnabled?: boolean;
|
||||
perfectDrawEnabled?: boolean;
|
||||
};
|
||||
export type ShapeGetClientRectConfig = GetClientRectConfig;
|
||||
export type FillFuncOutput = void | [Path2D | CanvasFillRule] | [Path2D, CanvasFillRule];
|
||||
export declare const shapes: {
|
||||
[key: string]: Shape;
|
||||
};
|
||||
/**
|
||||
* Shape constructor. Shapes are primitive objects such as rectangles,
|
||||
* circles, text, lines, etc.
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Node
|
||||
* @param {Object} config
|
||||
* @@shapeParams
|
||||
* @@nodeParams
|
||||
* @example
|
||||
* var customShape = new Konva.Shape({
|
||||
* x: 5,
|
||||
* y: 10,
|
||||
* fill: 'red',
|
||||
* // a Konva.Canvas renderer is passed into the sceneFunc function
|
||||
* sceneFunc (context, shape) {
|
||||
* context.beginPath();
|
||||
* context.moveTo(200, 50);
|
||||
* context.lineTo(420, 80);
|
||||
* context.quadraticCurveTo(300, 100, 260, 170);
|
||||
* context.closePath();
|
||||
* // Konva specific method
|
||||
* context.fillStrokeShape(shape);
|
||||
* }
|
||||
*});
|
||||
*/
|
||||
export declare class Shape<Config extends ShapeConfig = ShapeConfig> extends Node<Config> {
|
||||
_centroid: boolean;
|
||||
colorKey: string;
|
||||
_fillFunc: (ctx: Context) => FillFuncOutput;
|
||||
_strokeFunc: (ctx: Context) => void;
|
||||
_fillFuncHit: (ctx: Context) => void;
|
||||
_strokeFuncHit: (ctx: Context) => void;
|
||||
constructor(config?: Config);
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
getContext(): Context;
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
getCanvas(): SceneCanvas;
|
||||
getSceneFunc(): any;
|
||||
getHitFunc(): any;
|
||||
/**
|
||||
* returns whether or not a shadow will be rendered
|
||||
* @method
|
||||
* @name Konva.Shape#hasShadow
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
hasShadow(): any;
|
||||
_hasShadow(): boolean;
|
||||
_getFillPattern(): any;
|
||||
__getFillPattern(): CanvasPattern | null | undefined;
|
||||
_getLinearGradient(): any;
|
||||
__getLinearGradient(): CanvasGradient | undefined;
|
||||
_getRadialGradient(): any;
|
||||
__getRadialGradient(): CanvasGradient | undefined;
|
||||
getShadowRGBA(): any;
|
||||
_getShadowRGBA(): string | undefined;
|
||||
/**
|
||||
* returns whether or not the shape will be filled
|
||||
* @method
|
||||
* @name Konva.Shape#hasFill
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
hasFill(): any;
|
||||
_hasFill(): boolean;
|
||||
/**
|
||||
* returns whether or not the shape will be stroked
|
||||
* @method
|
||||
* @name Konva.Shape#hasStroke
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
hasStroke(): any;
|
||||
_hasStroke(): boolean;
|
||||
hasHitStroke(): any;
|
||||
/**
|
||||
* determines if point is in the shape, regardless if other shapes are on top of it. Note: because
|
||||
* this method clears a temporary canvas and then redraws the shape, it performs very poorly if executed many times
|
||||
* consecutively. Please use the {@link Konva.Stage#getIntersection} method if at all possible
|
||||
* because it performs much better. A shape that is invisible, not listening or not on a stage
|
||||
* never intersects
|
||||
* @method
|
||||
* @name Konva.Shape#intersects
|
||||
* @param {Object} point
|
||||
* @param {Number} point.x
|
||||
* @param {Number} point.y
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
intersects(point: Vector2d): boolean;
|
||||
destroy(): this;
|
||||
_useBufferCanvas(forceFill?: boolean, opacity?: any): boolean;
|
||||
setStrokeHitEnabled(val: number): void;
|
||||
getStrokeHitEnabled(): boolean;
|
||||
/**
|
||||
* return self rectangle (x, y, width, height) of shape.
|
||||
* This method are not taken into account transformation and styles.
|
||||
* @method
|
||||
* @name Konva.Shape#getSelfRect
|
||||
* @returns {Object} rect with {x, y, width, height} properties
|
||||
* @example
|
||||
*
|
||||
* rect.getSelfRect(); // return {x:0, y:0, width:rect.width(), height:rect.height()}
|
||||
* circle.getSelfRect(); // return {x: - circle.width() / 2, y: - circle.height() / 2, width:circle.width(), height:circle.height()}
|
||||
*
|
||||
*/
|
||||
getSelfRect(): IRect;
|
||||
_getSelfRectForDrawing(): IRect;
|
||||
getClientRect(config?: ShapeGetClientRectConfig): IRect;
|
||||
_getStrokePadding(miterLimit?: number): number;
|
||||
drawScene(can?: SceneCanvas, top?: Node): this;
|
||||
drawHit(can?: HitCanvas, top?: Node): this;
|
||||
/**
|
||||
* draw hit graph using the cached scene canvas
|
||||
* @method
|
||||
* @name Konva.Shape#drawHitFromCache
|
||||
* @param {Integer} alphaThreshold alpha channel threshold that determines whether or not
|
||||
* a pixel should be drawn onto the hit graph. Must be a value between 0 and 255.
|
||||
* The default is 0
|
||||
* @returns {Konva.Shape}
|
||||
* @example
|
||||
* shape.cache();
|
||||
* shape.drawHitFromCache();
|
||||
*/
|
||||
drawHitFromCache(alphaThreshold?: number): this;
|
||||
hasPointerCapture(pointerId: number): boolean;
|
||||
setPointerCapture(pointerId: number): void;
|
||||
releaseCapture(pointerId: number): void;
|
||||
draggable: GetSet<boolean, this>;
|
||||
embossBlend: GetSet<boolean, this>;
|
||||
dash: GetSet<number[], this>;
|
||||
dashEnabled: GetSet<boolean, this>;
|
||||
dashOffset: GetSet<number, this>;
|
||||
fill: GetSet<string | CanvasGradient, this>;
|
||||
fillEnabled: GetSet<boolean, this>;
|
||||
fillLinearGradientColorStops: GetSet<Array<number | string>, this>;
|
||||
fillLinearGradientStartPoint: GetSet<Vector2d, this>;
|
||||
fillLinearGradientStartPointX: GetSet<number, this>;
|
||||
fillLinearGradientStartPointY: GetSet<number, this>;
|
||||
fillLinearGradientEndPoint: GetSet<Vector2d, this>;
|
||||
fillLinearGradientEndPointX: GetSet<number, this>;
|
||||
fillLinearGradientEndPointY: GetSet<number, this>;
|
||||
fillPatternImage: GetSet<HTMLImageElement | HTMLCanvasElement, this>;
|
||||
fillRadialGradientStartRadius: GetSet<number, this>;
|
||||
fillRadialGradientEndRadius: GetSet<number, this>;
|
||||
fillRadialGradientColorStops: GetSet<Array<number | string>, this>;
|
||||
fillRadialGradientStartPoint: GetSet<Vector2d, this>;
|
||||
fillRadialGradientStartPointX: GetSet<number, this>;
|
||||
fillRadialGradientStartPointY: GetSet<number, this>;
|
||||
fillRadialGradientEndPoint: GetSet<Vector2d, this>;
|
||||
fillRadialGradientEndPointX: GetSet<number, this>;
|
||||
fillRadialGradientEndPointY: GetSet<number, this>;
|
||||
fillPatternOffset: GetSet<Vector2d, this>;
|
||||
fillPatternOffsetX: GetSet<number, this>;
|
||||
fillPatternOffsetY: GetSet<number, this>;
|
||||
fillPatternRepeat: GetSet<string, this>;
|
||||
fillPatternRotation: GetSet<number, this>;
|
||||
fillPatternScale: GetSet<Vector2d, this>;
|
||||
fillPatternScaleX: GetSet<number, this>;
|
||||
fillPatternScaleY: GetSet<number, this>;
|
||||
fillPatternX: GetSet<number, this>;
|
||||
fillPatternY: GetSet<number, this>;
|
||||
fillPriority: GetSet<string, this>;
|
||||
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>;
|
||||
selfRectFunc: GetSet<((shape: Shape) => IRect) | undefined, this>;
|
||||
shadowColor: GetSet<string, this>;
|
||||
shadowEnabled: GetSet<boolean, this>;
|
||||
shadowForStrokeEnabled: GetSet<boolean, this>;
|
||||
shadowOffset: GetSet<Vector2d, this>;
|
||||
shadowOffsetX: GetSet<number, this>;
|
||||
shadowOffsetY: GetSet<number, this>;
|
||||
shadowOpacity: GetSet<number, this>;
|
||||
shadowBlur: GetSet<number, this>;
|
||||
stroke: GetSet<string | CanvasGradient, this>;
|
||||
strokeEnabled: GetSet<boolean, this>;
|
||||
fillAfterStrokeEnabled: GetSet<boolean, this>;
|
||||
strokeScaleEnabled: GetSet<boolean, this>;
|
||||
strokeHitEnabled: GetSet<boolean, this>;
|
||||
strokeWidth: GetSet<number, this>;
|
||||
hitStrokeWidth: GetSet<number | 'auto', this>;
|
||||
strokeLinearGradientStartPoint: GetSet<Vector2d, this>;
|
||||
strokeLinearGradientEndPoint: GetSet<Vector2d, this>;
|
||||
strokeLinearGradientColorStops: GetSet<Array<number | string>, this>;
|
||||
strokeLinearGradientStartPointX: GetSet<number, this>;
|
||||
strokeLinearGradientStartPointY: GetSet<number, this>;
|
||||
strokeLinearGradientEndPointX: GetSet<number, this>;
|
||||
strokeLinearGradientEndPointY: GetSet<number, this>;
|
||||
fillRule: GetSet<CanvasFillRule, this>;
|
||||
}
|
||||
+1706
File diff suppressed because it is too large
Load Diff
+175
@@ -0,0 +1,175 @@
|
||||
import type { ContainerConfig } from './Container.ts';
|
||||
import { Container } from './Container.ts';
|
||||
import { SceneCanvas, HitCanvas } from './Canvas.ts';
|
||||
import type { GetSet, Vector2d } from './types.ts';
|
||||
import type { Shape } from './Shape.ts';
|
||||
import type { Layer } from './Layer.ts';
|
||||
export interface StageConfig extends ContainerConfig {
|
||||
container?: HTMLDivElement | string;
|
||||
eventBatchFunc?: (callback: () => void) => void;
|
||||
}
|
||||
type Click = {
|
||||
shape: Shape | null;
|
||||
time: number;
|
||||
};
|
||||
type EventType = 'mouse' | 'touch' | 'pointer';
|
||||
type PointerState = {
|
||||
targetShape?: Shape;
|
||||
downPosition?: Vector2d;
|
||||
pointerType?: string;
|
||||
relatedPointer?: PointerState;
|
||||
clickStartShape?: Shape | null;
|
||||
lastClick?: Click;
|
||||
};
|
||||
export declare const stages: Stage[];
|
||||
/**
|
||||
* Stage constructor. A stage is used to contain multiple layers
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Container
|
||||
* @param {Object} config
|
||||
* @param {String|Element} config.container Container selector or DOM element
|
||||
* @@nodeParams
|
||||
* @example
|
||||
* var stage = new Konva.Stage({
|
||||
* width: 500,
|
||||
* height: 800,
|
||||
* container: 'containerId' // or "#containerId" or ".containerClass"
|
||||
* });
|
||||
*/
|
||||
export declare class Stage extends Container<Layer, StageConfig> {
|
||||
content: HTMLDivElement;
|
||||
pointerPos: Vector2d | null;
|
||||
_pointerPositions: (Vector2d & {
|
||||
id?: number;
|
||||
})[];
|
||||
_changedPointerPositions: (Vector2d & {
|
||||
id: number;
|
||||
})[];
|
||||
_pointerEventType?: EventType;
|
||||
_pointerStates: Map<string, PointerState>;
|
||||
_lastClicks: Partial<Record<EventType, Click>>;
|
||||
bufferHitCanvas: HitCanvas;
|
||||
constructor(config: StageConfig);
|
||||
_validateAdd(child: any): void;
|
||||
_checkVisibility(): void;
|
||||
/**
|
||||
* The window the stage is rendered in. It is not always the window Konva was
|
||||
* imported into: the container may belong to an iframe, or to a window
|
||||
* opened with `window.open`.
|
||||
*/
|
||||
_getOwnerWindow(): Window | null;
|
||||
/**
|
||||
* set container dom element which contains the stage wrapper div element
|
||||
* @method
|
||||
* @name Konva.Stage#setContainer
|
||||
* @param {DomElement} container can pass in a dom element or id string
|
||||
*/
|
||||
setContainer(container: any): this;
|
||||
shouldDrawHit(): boolean;
|
||||
/**
|
||||
* clear all layers
|
||||
* @method
|
||||
* @name Konva.Stage#clear
|
||||
*/
|
||||
clear(): this;
|
||||
clone(obj?: any): this;
|
||||
destroy(): this;
|
||||
/**
|
||||
* returns ABSOLUTE pointer position which can be a touch position or mouse position
|
||||
* pointer position doesn't include any transforms (such as scale) of the stage
|
||||
* it is just a plain position of pointer relative to top-left corner of the canvas
|
||||
* @method
|
||||
* @name Konva.Stage#getPointerPosition
|
||||
* @returns {Vector2d|null}
|
||||
*/
|
||||
getPointerPosition(): Vector2d | null;
|
||||
_getPointerById(id?: number): (Vector2d & {
|
||||
id?: number;
|
||||
}) | undefined;
|
||||
getPointersPositions(): (Vector2d & {
|
||||
id?: number;
|
||||
})[];
|
||||
getStage(): this;
|
||||
getContent(): HTMLDivElement;
|
||||
_toKonvaCanvas(config: any): SceneCanvas;
|
||||
/**
|
||||
* get visible intersection shape. This is the preferred
|
||||
* method for determining if a point intersects a shape or not.
|
||||
* It reads the hit canvas, so it finds what pointer events would: nodes with
|
||||
* listening set to false or invisible nodes are not detected, a shape with opacity 0
|
||||
* is, and `hitStrokeWidth` counts. The position is relative to the top left corner of the
|
||||
* stage container, like `stage.getPointerPosition()`, without the stage transform
|
||||
* @method
|
||||
* @name Konva.Stage#getIntersection
|
||||
* @param {Object} pos
|
||||
* @param {Number} pos.x
|
||||
* @param {Number} pos.y
|
||||
* @returns {Konva.Node}
|
||||
* @example
|
||||
* var shape = stage.getIntersection({x: 50, y: 50});
|
||||
*/
|
||||
getIntersection(pos: Vector2d): Shape<import("./Shape.ts").ShapeConfig> | null;
|
||||
_resizeDOM(): void;
|
||||
add(layer: Layer, ...rest: any[]): this;
|
||||
getParent(): null;
|
||||
getLayer(): null;
|
||||
hasPointerCapture(pointerId: number): boolean;
|
||||
setPointerCapture(pointerId: number): void;
|
||||
releaseCapture(pointerId: number): void;
|
||||
/**
|
||||
* returns an array of layers
|
||||
* @method
|
||||
* @name Konva.Stage#getLayers
|
||||
*/
|
||||
getLayers(): Layer[];
|
||||
_bindContentEvents(): void;
|
||||
_batchEvents(callback: () => void): void;
|
||||
_pointerenter(evt: PointerEvent): void;
|
||||
_pointerover(evt: any): void;
|
||||
_pointerEventsEnabled(): boolean;
|
||||
_cancelClick(pointerId: number | undefined, eventType?: EventType): void;
|
||||
_pointerleave(evt: any): void;
|
||||
_pointerdown(evt: TouchEvent | MouseEvent | PointerEvent): void;
|
||||
_pointermove(evt: TouchEvent | MouseEvent | PointerEvent): void;
|
||||
_pointerup(evt: any): void;
|
||||
_contextmenu(evt: any): void;
|
||||
_wheel(evt: any): void;
|
||||
_pointercancel(evt: PointerEvent | TouchEvent): void;
|
||||
_lostpointercapture(evt: PointerEvent): void;
|
||||
/**
|
||||
* manually register pointers positions (mouse/touch) in the stage.
|
||||
* So you can use stage.getPointerPosition(). Usually you don't need to use that method
|
||||
* because all internal events are automatically registered. It may be useful if event
|
||||
* is triggered outside of the stage, but you still want to use Konva methods to get pointers position.
|
||||
* @method
|
||||
* @name Konva.Stage#setPointersPositions
|
||||
* @param {Object} evt Event object
|
||||
* @example
|
||||
*
|
||||
* window.addEventListener('mousemove', (e) => {
|
||||
* stage.setPointersPositions(e);
|
||||
* });
|
||||
*/
|
||||
setPointersPositions(evt: any): void;
|
||||
_setPointerPosition(evt: any): void;
|
||||
_getContentPosition(): {
|
||||
top: number;
|
||||
left: number;
|
||||
scaleX: number;
|
||||
scaleY: number;
|
||||
};
|
||||
_buildDOM(): void;
|
||||
cache(): this;
|
||||
clearCache(): this;
|
||||
/**
|
||||
* batch draw
|
||||
* @method
|
||||
* @name Konva.Stage#batchDraw
|
||||
* @return {Konva.Stage} this
|
||||
*/
|
||||
batchDraw(): this;
|
||||
container: GetSet<HTMLDivElement, this, HTMLDivElement | string>;
|
||||
eventBatchFunc: GetSet<((callback: () => void) => void) | undefined, this>;
|
||||
}
|
||||
export {};
|
||||
+1006
File diff suppressed because it is too large
Load Diff
+245
@@ -0,0 +1,245 @@
|
||||
import { Animation } from './Animation.ts';
|
||||
import type { NodeConfig } from './Node.ts';
|
||||
import { Node } from './Node.ts';
|
||||
declare class TweenEngine {
|
||||
prop: string;
|
||||
propFunc: Function;
|
||||
begin: number;
|
||||
_pos: number;
|
||||
duration: number;
|
||||
prevPos: number;
|
||||
yoyo: boolean;
|
||||
_time: number;
|
||||
_position: number;
|
||||
_startTime: number;
|
||||
_finish: number;
|
||||
func: Function;
|
||||
_change: number;
|
||||
state: number;
|
||||
onPlay: Function;
|
||||
onReverse: Function;
|
||||
onPause: Function;
|
||||
onReset: Function;
|
||||
onFinish: Function;
|
||||
onUpdate: Function;
|
||||
constructor(prop: any, propFunc: any, func: any, begin: any, finish: any, duration: any, yoyo: any);
|
||||
fire(str: any): void;
|
||||
setTime(t: any): void;
|
||||
getTime(): number;
|
||||
setPosition(p: any): void;
|
||||
getPosition(t: any): any;
|
||||
play(): void;
|
||||
reverse(): void;
|
||||
seek(t: any): void;
|
||||
reset(): void;
|
||||
finish(): void;
|
||||
update(): void;
|
||||
onEnterFrame(): void;
|
||||
pause(): void;
|
||||
getTimer(): number;
|
||||
}
|
||||
export interface TweenOwnConfig {
|
||||
node: Node;
|
||||
duration?: number;
|
||||
easing?: (typeof Easings)[keyof typeof Easings];
|
||||
yoyo?: boolean;
|
||||
onFinish?: Function;
|
||||
onUpdate?: Function;
|
||||
onReset?: Function;
|
||||
}
|
||||
export interface TweenConfig extends NodeConfig, TweenOwnConfig {
|
||||
}
|
||||
/**
|
||||
* Tween constructor. Tweens enable you to animate a node between the current state and a new state.
|
||||
* You can play, pause, reverse, seek, reset, and finish tweens. By default, tweens are animated using
|
||||
* a linear easing. For more tweening options, check out {@link Konva.Easings}
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* // instantiate new tween which fully rotates a node in 1 second
|
||||
* var tween = new Konva.Tween({
|
||||
* // list of tween specific properties
|
||||
* node: node,
|
||||
* duration: 1,
|
||||
* easing: Konva.Easings.EaseInOut,
|
||||
* yoyo: false, // play back and forth
|
||||
* onUpdate: () => console.log('node attrs updated'),
|
||||
* onFinish: () => console.log('finished'),
|
||||
* // set new values for any attributes of a passed node
|
||||
* rotation: 360,
|
||||
* fill: 'red'
|
||||
* });
|
||||
*
|
||||
* // play tween
|
||||
* tween.play();
|
||||
*
|
||||
* // pause tween
|
||||
* tween.pause();
|
||||
*/
|
||||
export declare class Tween {
|
||||
static attrs: {};
|
||||
static tweens: {};
|
||||
node: Node;
|
||||
anim: Animation;
|
||||
tween: TweenEngine;
|
||||
_id: number;
|
||||
onFinish: Function | undefined;
|
||||
onReset: Function | undefined;
|
||||
onUpdate: Function | undefined;
|
||||
constructor(config: TweenConfig);
|
||||
_addAttr(key: any, end: any): void;
|
||||
_tweenFunc(i: any): void;
|
||||
_addListeners(): void;
|
||||
/**
|
||||
* play
|
||||
* @method
|
||||
* @name Konva.Tween#play
|
||||
* @returns {Tween}
|
||||
*/
|
||||
play(): this;
|
||||
/**
|
||||
* reverse
|
||||
* @method
|
||||
* @name Konva.Tween#reverse
|
||||
* @returns {Tween}
|
||||
*/
|
||||
reverse(): this;
|
||||
/**
|
||||
* reset
|
||||
* @method
|
||||
* @name Konva.Tween#reset
|
||||
* @returns {Tween}
|
||||
*/
|
||||
reset(): this;
|
||||
/**
|
||||
* seek
|
||||
* @method
|
||||
* @name Konva.Tween#seek
|
||||
* @param {Number} t time in seconds between 0 and the duration
|
||||
* @returns {Tween}
|
||||
*/
|
||||
seek(t: any): this;
|
||||
/**
|
||||
* pause
|
||||
* @method
|
||||
* @name Konva.Tween#pause
|
||||
* @returns {Tween}
|
||||
*/
|
||||
pause(): this;
|
||||
/**
|
||||
* finish
|
||||
* @method
|
||||
* @name Konva.Tween#finish
|
||||
* @returns {Tween}
|
||||
*/
|
||||
finish(): this;
|
||||
/**
|
||||
* destroy
|
||||
* @method
|
||||
* @name Konva.Tween#destroy
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
/**
|
||||
* @namespace Easings
|
||||
* @memberof Konva
|
||||
*/
|
||||
export declare const Easings: {
|
||||
/**
|
||||
* back ease in
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
BackEaseIn(t: any, b: any, c: any, d: any): any;
|
||||
/**
|
||||
* back ease out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
BackEaseOut(t: any, b: any, c: any, d: any): any;
|
||||
/**
|
||||
* back ease in out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
BackEaseInOut(t: any, b: any, c: any, d: any): any;
|
||||
/**
|
||||
* elastic ease in
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
ElasticEaseIn(t: any, b: any, c: any, d: any, a: any, p: any): any;
|
||||
/**
|
||||
* elastic ease out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
ElasticEaseOut(t: any, b: any, c: any, d: any, a: any, p: any): any;
|
||||
/**
|
||||
* elastic ease in out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
ElasticEaseInOut(t: any, b: any, c: any, d: any, a: any, p: any): any;
|
||||
/**
|
||||
* bounce ease out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
BounceEaseOut(t: any, b: any, c: any, d: any): any;
|
||||
/**
|
||||
* bounce ease in
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
BounceEaseIn(t: any, b: any, c: any, d: any): any;
|
||||
/**
|
||||
* bounce ease in out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
BounceEaseInOut(t: any, b: any, c: any, d: any): any;
|
||||
/**
|
||||
* ease in
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
EaseIn(t: any, b: any, c: any, d: any): any;
|
||||
/**
|
||||
* ease out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
EaseOut(t: any, b: any, c: any, d: any): any;
|
||||
/**
|
||||
* ease in out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
EaseInOut(t: any, b: any, c: any, d: any): any;
|
||||
/**
|
||||
* strong ease in
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
StrongEaseIn(t: any, b: any, c: any, d: any): any;
|
||||
/**
|
||||
* strong ease out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
StrongEaseOut(t: any, b: any, c: any, d: any): any;
|
||||
/**
|
||||
* strong ease in out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
StrongEaseInOut(t: any, b: any, c: any, d: any): any;
|
||||
/**
|
||||
* linear
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
Linear(t: any, b: any, c: any, d: any): any;
|
||||
};
|
||||
export {};
|
||||
+749
@@ -0,0 +1,749 @@
|
||||
import { Util } from "./Util.js";
|
||||
import { Animation } from "./Animation.js";
|
||||
import { Node } from "./Node.js";
|
||||
import { Konva } from "./Global.js";
|
||||
// every config key that is not an attribute to tween
|
||||
const blacklist = {
|
||||
node: 1,
|
||||
duration: 1,
|
||||
easing: 1,
|
||||
onFinish: 1,
|
||||
onUpdate: 1,
|
||||
onReset: 1,
|
||||
yoyo: 1,
|
||||
}, PAUSED = 1, PLAYING = 2, REVERSING = 3, colorAttrs = ['fill', 'stroke', 'shadowColor'];
|
||||
let idCounter = 0;
|
||||
// the diff below reads r/g/b/a directly, so a color we can not parse must fail
|
||||
// with its own message instead of "Cannot read properties of undefined"
|
||||
function colorToRGBA(color) {
|
||||
return (Util.colorToRGBA(color) ||
|
||||
Util.throw('can not tween the color "' +
|
||||
color +
|
||||
'", because it is not a valid color.'));
|
||||
}
|
||||
function colorDiff(start, end) {
|
||||
return {
|
||||
r: end.r - start.r,
|
||||
g: end.g - start.g,
|
||||
b: end.b - start.b,
|
||||
a: end.a - start.a,
|
||||
};
|
||||
}
|
||||
function tweenColor(start, diff, i) {
|
||||
return ('rgba(' +
|
||||
Math.round(start.r + diff.r * i) +
|
||||
',' +
|
||||
Math.round(start.g + diff.g * i) +
|
||||
',' +
|
||||
Math.round(start.b + diff.b * i) +
|
||||
',' +
|
||||
(start.a + diff.a * i) +
|
||||
')');
|
||||
}
|
||||
class TweenEngine {
|
||||
constructor(prop, propFunc, func, begin, finish, duration, yoyo) {
|
||||
this.prop = prop;
|
||||
this.propFunc = propFunc;
|
||||
this.begin = begin;
|
||||
this._pos = begin;
|
||||
this.duration = duration;
|
||||
this._change = 0;
|
||||
this.prevPos = 0;
|
||||
this.yoyo = yoyo;
|
||||
this._time = 0;
|
||||
this._position = 0;
|
||||
this._startTime = 0;
|
||||
this._finish = 0;
|
||||
this.func = func;
|
||||
this._change = finish - this.begin;
|
||||
this.pause();
|
||||
}
|
||||
fire(str) {
|
||||
const handler = this[str];
|
||||
if (handler) {
|
||||
handler();
|
||||
}
|
||||
}
|
||||
setTime(t) {
|
||||
if (t > this.duration) {
|
||||
if (this.yoyo) {
|
||||
this._time = this.duration;
|
||||
this.reverse();
|
||||
}
|
||||
else {
|
||||
this.finish();
|
||||
}
|
||||
}
|
||||
else if (t < 0) {
|
||||
if (this.yoyo) {
|
||||
this._time = 0;
|
||||
this.play();
|
||||
}
|
||||
else {
|
||||
this.reset();
|
||||
}
|
||||
}
|
||||
else {
|
||||
this._time = t;
|
||||
this.update();
|
||||
}
|
||||
}
|
||||
getTime() {
|
||||
return this._time;
|
||||
}
|
||||
setPosition(p) {
|
||||
this.prevPos = this._pos;
|
||||
this.propFunc(p);
|
||||
this._pos = p;
|
||||
}
|
||||
getPosition(t) {
|
||||
if (t === undefined) {
|
||||
t = this._time;
|
||||
}
|
||||
return this.func(t, this.begin, this._change, this.duration);
|
||||
}
|
||||
play() {
|
||||
this.state = PLAYING;
|
||||
this._startTime = this.getTimer() - this._time;
|
||||
this.fire('onPlay');
|
||||
this.onEnterFrame();
|
||||
}
|
||||
reverse() {
|
||||
this.state = REVERSING;
|
||||
this._time = this.duration - this._time;
|
||||
this._startTime = this.getTimer() - this._time;
|
||||
this.fire('onReverse');
|
||||
this.onEnterFrame();
|
||||
}
|
||||
seek(t) {
|
||||
this.pause();
|
||||
this._time = t;
|
||||
this.update();
|
||||
this.fire('onSeek');
|
||||
}
|
||||
reset() {
|
||||
this.pause();
|
||||
this._time = 0;
|
||||
this.update();
|
||||
this.fire('onReset');
|
||||
}
|
||||
finish() {
|
||||
this.pause();
|
||||
this._time = this.duration;
|
||||
this.update();
|
||||
this.fire('onFinish');
|
||||
}
|
||||
update() {
|
||||
this.setPosition(this.getPosition(this._time));
|
||||
this.fire('onUpdate');
|
||||
}
|
||||
onEnterFrame() {
|
||||
const t = this.getTimer() - this._startTime;
|
||||
if (this.state === PLAYING) {
|
||||
this.setTime(t);
|
||||
}
|
||||
else if (this.state === REVERSING) {
|
||||
this.setTime(this.duration - t);
|
||||
}
|
||||
}
|
||||
pause() {
|
||||
this.state = PAUSED;
|
||||
this.fire('onPause');
|
||||
}
|
||||
getTimer() {
|
||||
return Date.now();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Tween constructor. Tweens enable you to animate a node between the current state and a new state.
|
||||
* You can play, pause, reverse, seek, reset, and finish tweens. By default, tweens are animated using
|
||||
* a linear easing. For more tweening options, check out {@link Konva.Easings}
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @example
|
||||
* // instantiate new tween which fully rotates a node in 1 second
|
||||
* var tween = new Konva.Tween({
|
||||
* // list of tween specific properties
|
||||
* node: node,
|
||||
* duration: 1,
|
||||
* easing: Konva.Easings.EaseInOut,
|
||||
* yoyo: false, // play back and forth
|
||||
* onUpdate: () => console.log('node attrs updated'),
|
||||
* onFinish: () => console.log('finished'),
|
||||
* // set new values for any attributes of a passed node
|
||||
* rotation: 360,
|
||||
* fill: 'red'
|
||||
* });
|
||||
*
|
||||
* // play tween
|
||||
* tween.play();
|
||||
*
|
||||
* // pause tween
|
||||
* tween.pause();
|
||||
*/
|
||||
export class Tween {
|
||||
constructor(config) {
|
||||
const that = this, node = config.node, nodeId = node._id, easing = config.easing || Easings.Linear, yoyo = !!config.yoyo;
|
||||
let duration, key;
|
||||
if (typeof config.duration === 'undefined') {
|
||||
duration = 0.3;
|
||||
}
|
||||
else if (config.duration === 0) {
|
||||
// zero is bad value for duration
|
||||
duration = 0.001;
|
||||
}
|
||||
else {
|
||||
duration = config.duration;
|
||||
}
|
||||
this.node = node;
|
||||
this._id = idCounter++;
|
||||
const layers = node.getLayer() ||
|
||||
(node instanceof Konva['Stage'] ? node.getLayers() : null);
|
||||
if (!layers) {
|
||||
Util.error('Tween constructor have `node` that is not in a layer. Please add node into layer first.');
|
||||
}
|
||||
this.anim = new Animation(function () {
|
||||
that.tween.onEnterFrame();
|
||||
}, layers);
|
||||
this.tween = new TweenEngine(key, function (i) {
|
||||
that._tweenFunc(i);
|
||||
}, easing, 0, 1, duration * 1000, yoyo);
|
||||
this._addListeners();
|
||||
// init attrs map
|
||||
if (!Tween.attrs[nodeId]) {
|
||||
Tween.attrs[nodeId] = {};
|
||||
}
|
||||
if (!Tween.attrs[nodeId][this._id]) {
|
||||
Tween.attrs[nodeId][this._id] = {};
|
||||
}
|
||||
// init tweens map
|
||||
if (!Tween.tweens[nodeId]) {
|
||||
Tween.tweens[nodeId] = {};
|
||||
}
|
||||
for (key in config) {
|
||||
if (blacklist[key] === undefined) {
|
||||
this._addAttr(key, config[key]);
|
||||
}
|
||||
}
|
||||
this.reset();
|
||||
// callbacks
|
||||
this.onFinish = config.onFinish;
|
||||
this.onReset = config.onReset;
|
||||
this.onUpdate = config.onUpdate;
|
||||
}
|
||||
_addAttr(key, end) {
|
||||
// a component attribute ({x, y} of scale, offset, a gradient point...)
|
||||
// is tweened through its components: scale -> scaleX, scaleY
|
||||
if (Util._isPlainObject(end)) {
|
||||
for (const component in end) {
|
||||
this._addAttr(key + Util._capitalize(component), end[component]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const node = this.node, nodeId = node._id;
|
||||
let diff, len, trueEnd, trueStart;
|
||||
// remove conflict from tween map if it exists
|
||||
const tweenId = Tween.tweens[nodeId][key];
|
||||
if (tweenId !== undefined) {
|
||||
delete Tween.attrs[nodeId][tweenId][key];
|
||||
}
|
||||
// add to tween map
|
||||
let start = node.getAttr(key);
|
||||
if (Util._isArray(end) || Util._isArray(start)) {
|
||||
diff = [];
|
||||
// only normalized arrays are interpolated, so the values the user asked
|
||||
// for are restored when the tween ends
|
||||
trueStart = start;
|
||||
trueEnd = end;
|
||||
// an attribute the node does not have yet starts from zeros, a scalar
|
||||
// side tweens every entry. A copy of start, as the colour stops are
|
||||
// replaced by RGBA objects below
|
||||
start = Util._isArray(start)
|
||||
? start.slice()
|
||||
: new Array(end.length).fill(start || 0);
|
||||
if (!Util._isArray(end)) {
|
||||
end = new Array(start.length).fill(end);
|
||||
}
|
||||
len = Math.max(end.length, start.length);
|
||||
if (key === 'points' && end.length !== start.length) {
|
||||
// before tweening points we need to make sure that start.length === end.length
|
||||
// Util._prepareArrayForTween thinking that end.length > start.length
|
||||
if (end.length > start.length) {
|
||||
// so in this case we will increase number of starting points
|
||||
start = Util._prepareArrayForTween(start, end, node.closed());
|
||||
}
|
||||
else {
|
||||
// in this case we will increase number of eding points
|
||||
end = Util._prepareArrayForTween(end, start, node.closed());
|
||||
}
|
||||
}
|
||||
if (key.endsWith('ColorStops')) {
|
||||
for (let n = 0; n < len; n++) {
|
||||
if (n % 2 === 0) {
|
||||
diff.push((end[n] || 0) - (start[n] || 0));
|
||||
}
|
||||
else {
|
||||
const startRGBA = colorToRGBA(start[n]);
|
||||
start[n] = startRGBA;
|
||||
diff.push(colorDiff(startRGBA, colorToRGBA(end[n])));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let n = 0; n < len; n++) {
|
||||
diff.push((end[n] || 0) - (start[n] || 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (colorAttrs.indexOf(key) !== -1) {
|
||||
start = colorToRGBA(start);
|
||||
diff = colorDiff(start, colorToRGBA(end));
|
||||
}
|
||||
else {
|
||||
diff = end - start;
|
||||
}
|
||||
Tween.attrs[nodeId][this._id][key] = {
|
||||
start: start,
|
||||
diff: diff,
|
||||
end: end,
|
||||
trueEnd: trueEnd,
|
||||
trueStart: trueStart,
|
||||
};
|
||||
Tween.tweens[nodeId][key] = this._id;
|
||||
}
|
||||
_tweenFunc(i) {
|
||||
var _a;
|
||||
const node = this.node, attrs = (_a = Tween.attrs[node._id]) === null || _a === void 0 ? void 0 : _a[this._id];
|
||||
let key, attr, start, diff, newVal, n, len, end;
|
||||
for (key in attrs) {
|
||||
attr = attrs[key];
|
||||
start = attr.start;
|
||||
diff = attr.diff;
|
||||
end = attr.end;
|
||||
if (Util._isArray(start)) {
|
||||
newVal = [];
|
||||
len = Math.max(start.length, end.length);
|
||||
if (key.endsWith('ColorStops')) {
|
||||
for (n = 0; n < len; n++) {
|
||||
if (n % 2 === 0) {
|
||||
newVal.push((start[n] || 0) + diff[n] * i);
|
||||
}
|
||||
else {
|
||||
newVal.push(tweenColor(start[n], diff[n], i));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (n = 0; n < len; n++) {
|
||||
newVal.push((start[n] || 0) + diff[n] * i);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (colorAttrs.indexOf(key) !== -1) {
|
||||
newVal = tweenColor(start, diff, i);
|
||||
}
|
||||
else {
|
||||
newVal = start + diff * i;
|
||||
}
|
||||
node.setAttr(key, newVal);
|
||||
}
|
||||
}
|
||||
_addListeners() {
|
||||
// a running tween is destroyed with its node (the animation loop would
|
||||
// keep both alive); a stopped one is not held by the node at all.
|
||||
// The namespace contains "konva" so clone() does not copy the listener
|
||||
const destroyEvent = `destroy.konva-tween${this._id}`;
|
||||
const onDestroy = () => this.destroy();
|
||||
const start = () => {
|
||||
this.node.off(destroyEvent).on(destroyEvent, onDestroy);
|
||||
this.anim.start();
|
||||
};
|
||||
this.tween.onPlay = start;
|
||||
this.tween.onReverse = start;
|
||||
// stop listeners
|
||||
this.tween.onPause = () => {
|
||||
this.node.off(destroyEvent);
|
||||
this.anim.stop();
|
||||
};
|
||||
const end = (edge, callback) => {
|
||||
var _a;
|
||||
// no attributes means the tween was destroyed inside the last onUpdate
|
||||
const attrs = (_a = Tween.attrs[this.node._id]) === null || _a === void 0 ? void 0 : _a[this._id];
|
||||
if (!attrs) {
|
||||
return;
|
||||
}
|
||||
for (const key in attrs) {
|
||||
if (attrs[key][edge] !== undefined) {
|
||||
this.node.setAttr(key, attrs[key][edge]);
|
||||
}
|
||||
}
|
||||
callback === null || callback === void 0 ? void 0 : callback.call(this);
|
||||
};
|
||||
this.tween.onFinish = () => end('trueEnd', this.onFinish);
|
||||
this.tween.onReset = () => end('trueStart', this.onReset);
|
||||
this.tween.onUpdate = () => {
|
||||
if (this.onUpdate) {
|
||||
this.onUpdate.call(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* play
|
||||
* @method
|
||||
* @name Konva.Tween#play
|
||||
* @returns {Tween}
|
||||
*/
|
||||
play() {
|
||||
this.tween.play();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* reverse
|
||||
* @method
|
||||
* @name Konva.Tween#reverse
|
||||
* @returns {Tween}
|
||||
*/
|
||||
reverse() {
|
||||
this.tween.reverse();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* reset
|
||||
* @method
|
||||
* @name Konva.Tween#reset
|
||||
* @returns {Tween}
|
||||
*/
|
||||
reset() {
|
||||
this.tween.reset();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* seek
|
||||
* @method
|
||||
* @name Konva.Tween#seek
|
||||
* @param {Number} t time in seconds between 0 and the duration
|
||||
* @returns {Tween}
|
||||
*/
|
||||
seek(t) {
|
||||
this.tween.seek(t * 1000);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* pause
|
||||
* @method
|
||||
* @name Konva.Tween#pause
|
||||
* @returns {Tween}
|
||||
*/
|
||||
pause() {
|
||||
this.tween.pause();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* finish
|
||||
* @method
|
||||
* @name Konva.Tween#finish
|
||||
* @returns {Tween}
|
||||
*/
|
||||
finish() {
|
||||
this.tween.finish();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* destroy
|
||||
* @method
|
||||
* @name Konva.Tween#destroy
|
||||
*/
|
||||
destroy() {
|
||||
var _a;
|
||||
const nodeId = this.node._id, thisId = this._id, owned = (_a = Tween.attrs[nodeId]) === null || _a === void 0 ? void 0 : _a[thisId], owners = Tween.tweens[nodeId];
|
||||
this.pause();
|
||||
if (this.anim) {
|
||||
this.anim.stop();
|
||||
}
|
||||
// release only the attributes this tween still owns, other tweens on
|
||||
// the node keep theirs (nothing left to do when destroyed twice)
|
||||
if (owned) {
|
||||
if (owners) {
|
||||
for (const key in owned) {
|
||||
delete owners[key];
|
||||
}
|
||||
if (Object.keys(owners).length === 0) {
|
||||
delete Tween.tweens[nodeId];
|
||||
}
|
||||
}
|
||||
delete Tween.attrs[nodeId][thisId];
|
||||
if (Object.keys(Tween.attrs[nodeId]).length === 0) {
|
||||
delete Tween.attrs[nodeId];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Tween.attrs = {};
|
||||
Tween.tweens = {};
|
||||
/**
|
||||
* Tween node properties. Shorter usage of {@link Konva.Tween} object.
|
||||
*
|
||||
* @method Konva.Node#to
|
||||
* @param {Object} [params] tween params
|
||||
* @returns {Konva.Tween}
|
||||
* @example
|
||||
*
|
||||
* circle.to({
|
||||
* x : 50,
|
||||
* duration : 0.5,
|
||||
* onUpdate: () => console.log('props updated'),
|
||||
* onFinish: () => console.log('finished'),
|
||||
* });
|
||||
*/
|
||||
Node.prototype.to = function (params) {
|
||||
const onFinish = params.onFinish;
|
||||
// params stay untouched, so the same object can animate several nodes
|
||||
const tween = new Tween({
|
||||
...params,
|
||||
node: this,
|
||||
onFinish() {
|
||||
tween.destroy();
|
||||
if (onFinish) {
|
||||
onFinish();
|
||||
}
|
||||
},
|
||||
});
|
||||
tween.play();
|
||||
return tween;
|
||||
};
|
||||
/*
|
||||
* These eases were ported from an Adobe Flash tweening library to JavaScript
|
||||
* by Xaric
|
||||
*/
|
||||
/**
|
||||
* @namespace Easings
|
||||
* @memberof Konva
|
||||
*/
|
||||
export const Easings = {
|
||||
/**
|
||||
* back ease in
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
BackEaseIn(t, b, c, d) {
|
||||
const s = 1.70158;
|
||||
return c * (t /= d) * t * ((s + 1) * t - s) + b;
|
||||
},
|
||||
/**
|
||||
* back ease out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
BackEaseOut(t, b, c, d) {
|
||||
const s = 1.70158;
|
||||
return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
|
||||
},
|
||||
/**
|
||||
* back ease in out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
BackEaseInOut(t, b, c, d) {
|
||||
let s = 1.70158;
|
||||
if ((t /= d / 2) < 1) {
|
||||
return (c / 2) * (t * t * (((s *= 1.525) + 1) * t - s)) + b;
|
||||
}
|
||||
return (c / 2) * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2) + b;
|
||||
},
|
||||
/**
|
||||
* elastic ease in
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
ElasticEaseIn(t, b, c, d, a, p) {
|
||||
// added s = 0
|
||||
let s = 0;
|
||||
if (t === 0) {
|
||||
return b;
|
||||
}
|
||||
if ((t /= d) === 1) {
|
||||
return b + c;
|
||||
}
|
||||
if (!p) {
|
||||
p = d * 0.3;
|
||||
}
|
||||
if (!a || a < Math.abs(c)) {
|
||||
a = c;
|
||||
s = p / 4;
|
||||
}
|
||||
else {
|
||||
s = (p / (2 * Math.PI)) * Math.asin(c / a);
|
||||
}
|
||||
return (-(a *
|
||||
Math.pow(2, 10 * (t -= 1)) *
|
||||
Math.sin(((t * d - s) * (2 * Math.PI)) / p)) + b);
|
||||
},
|
||||
/**
|
||||
* elastic ease out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
ElasticEaseOut(t, b, c, d, a, p) {
|
||||
// added s = 0
|
||||
let s = 0;
|
||||
if (t === 0) {
|
||||
return b;
|
||||
}
|
||||
if ((t /= d) === 1) {
|
||||
return b + c;
|
||||
}
|
||||
if (!p) {
|
||||
p = d * 0.3;
|
||||
}
|
||||
if (!a || a < Math.abs(c)) {
|
||||
a = c;
|
||||
s = p / 4;
|
||||
}
|
||||
else {
|
||||
s = (p / (2 * Math.PI)) * Math.asin(c / a);
|
||||
}
|
||||
return (a * Math.pow(2, -10 * t) * Math.sin(((t * d - s) * (2 * Math.PI)) / p) +
|
||||
c +
|
||||
b);
|
||||
},
|
||||
/**
|
||||
* elastic ease in out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
ElasticEaseInOut(t, b, c, d, a, p) {
|
||||
// added s = 0
|
||||
let s = 0;
|
||||
if (t === 0) {
|
||||
return b;
|
||||
}
|
||||
if ((t /= d / 2) === 2) {
|
||||
return b + c;
|
||||
}
|
||||
if (!p) {
|
||||
p = d * (0.3 * 1.5);
|
||||
}
|
||||
if (!a || a < Math.abs(c)) {
|
||||
a = c;
|
||||
s = p / 4;
|
||||
}
|
||||
else {
|
||||
s = (p / (2 * Math.PI)) * Math.asin(c / a);
|
||||
}
|
||||
if (t < 1) {
|
||||
return (-0.5 *
|
||||
(a *
|
||||
Math.pow(2, 10 * (t -= 1)) *
|
||||
Math.sin(((t * d - s) * (2 * Math.PI)) / p)) +
|
||||
b);
|
||||
}
|
||||
return (a *
|
||||
Math.pow(2, -10 * (t -= 1)) *
|
||||
Math.sin(((t * d - s) * (2 * Math.PI)) / p) *
|
||||
0.5 +
|
||||
c +
|
||||
b);
|
||||
},
|
||||
/**
|
||||
* bounce ease out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
BounceEaseOut(t, b, c, d) {
|
||||
if ((t /= d) < 1 / 2.75) {
|
||||
return c * (7.5625 * t * t) + b;
|
||||
}
|
||||
else if (t < 2 / 2.75) {
|
||||
return c * (7.5625 * (t -= 1.5 / 2.75) * t + 0.75) + b;
|
||||
}
|
||||
else if (t < 2.5 / 2.75) {
|
||||
return c * (7.5625 * (t -= 2.25 / 2.75) * t + 0.9375) + b;
|
||||
}
|
||||
else {
|
||||
return c * (7.5625 * (t -= 2.625 / 2.75) * t + 0.984375) + b;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* bounce ease in
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
BounceEaseIn(t, b, c, d) {
|
||||
return c - Easings.BounceEaseOut(d - t, 0, c, d) + b;
|
||||
},
|
||||
/**
|
||||
* bounce ease in out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
BounceEaseInOut(t, b, c, d) {
|
||||
if (t < d / 2) {
|
||||
return Easings.BounceEaseIn(t * 2, 0, c, d) * 0.5 + b;
|
||||
}
|
||||
else {
|
||||
return Easings.BounceEaseOut(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* ease in
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
EaseIn(t, b, c, d) {
|
||||
return c * (t /= d) * t + b;
|
||||
},
|
||||
/**
|
||||
* ease out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
EaseOut(t, b, c, d) {
|
||||
return -c * (t /= d) * (t - 2) + b;
|
||||
},
|
||||
/**
|
||||
* ease in out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
EaseInOut(t, b, c, d) {
|
||||
if ((t /= d / 2) < 1) {
|
||||
return (c / 2) * t * t + b;
|
||||
}
|
||||
return (-c / 2) * (--t * (t - 2) - 1) + b;
|
||||
},
|
||||
/**
|
||||
* strong ease in
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
StrongEaseIn(t, b, c, d) {
|
||||
return c * (t /= d) * t * t * t * t + b;
|
||||
},
|
||||
/**
|
||||
* strong ease out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
StrongEaseOut(t, b, c, d) {
|
||||
return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
|
||||
},
|
||||
/**
|
||||
* strong ease in out
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
StrongEaseInOut(t, b, c, d) {
|
||||
if ((t /= d / 2) < 1) {
|
||||
return (c / 2) * t * t * t * t * t + b;
|
||||
}
|
||||
return (c / 2) * ((t -= 2) * t * t * t * t + 2) + b;
|
||||
},
|
||||
/**
|
||||
* linear
|
||||
* @function
|
||||
* @memberof Konva.Easings
|
||||
*/
|
||||
Linear(t, b, c, d) {
|
||||
return (c * t) / d + b;
|
||||
},
|
||||
};
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
import type { Context } from './Context.ts';
|
||||
import type { IRect, RGB, Vector2d } from './types.ts';
|
||||
/**
|
||||
* Transform constructor.
|
||||
* In most of the cases you don't need to use it in your app. Because it is for internal usage in Konva core.
|
||||
* But there is a documentation for that class in case you still want
|
||||
* to make some manual calculations.
|
||||
* @constructor
|
||||
* @param {Array} [m] Optional six-element matrix
|
||||
* @memberof Konva
|
||||
*/
|
||||
export declare class Transform {
|
||||
m: Array<number>;
|
||||
dirty: boolean;
|
||||
constructor(m?: Array<number>);
|
||||
reset(): void;
|
||||
/**
|
||||
* Copy Konva.Transform object
|
||||
* @method
|
||||
* @name Konva.Transform#copy
|
||||
* @returns {Konva.Transform}
|
||||
* @example
|
||||
* const tr = shape.getTransform().copy()
|
||||
*/
|
||||
copy(): Transform;
|
||||
copyInto(tr: Transform): void;
|
||||
/**
|
||||
* Transform point
|
||||
* @method
|
||||
* @name Konva.Transform#point
|
||||
* @param {Object} point 2D point(x, y)
|
||||
* @returns {Object} 2D point(x, y)
|
||||
*/
|
||||
point(point: Vector2d): {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
_getTransformedRect(rect: IRect): IRect;
|
||||
/**
|
||||
* Apply translation
|
||||
* @method
|
||||
* @name Konva.Transform#translate
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
* @returns {Konva.Transform}
|
||||
*/
|
||||
translate(x: number, y: number): this;
|
||||
/**
|
||||
* Apply scale
|
||||
* @method
|
||||
* @name Konva.Transform#scale
|
||||
* @param {Number} sx
|
||||
* @param {Number} sy
|
||||
* @returns {Konva.Transform}
|
||||
*/
|
||||
scale(sx: number, sy: number): this;
|
||||
/**
|
||||
* Apply rotation
|
||||
* @method
|
||||
* @name Konva.Transform#rotate
|
||||
* @param {Number} rad Angle in radians
|
||||
* @returns {Konva.Transform}
|
||||
*/
|
||||
rotate(rad: number): this;
|
||||
/**
|
||||
* Returns the translation
|
||||
* @method
|
||||
* @name Konva.Transform#getTranslation
|
||||
* @returns {Object} 2D point(x, y)
|
||||
*/
|
||||
getTranslation(): {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
/**
|
||||
* Apply skew
|
||||
* @method
|
||||
* @name Konva.Transform#skew
|
||||
* @param {Number} sx
|
||||
* @param {Number} sy
|
||||
* @returns {Konva.Transform}
|
||||
*/
|
||||
skew(sx: number, sy: number): this;
|
||||
/**
|
||||
* Transform multiplication
|
||||
* @method
|
||||
* @name Konva.Transform#multiply
|
||||
* @param {Konva.Transform} matrix
|
||||
* @returns {Konva.Transform}
|
||||
*/
|
||||
multiply(matrix: Transform): this;
|
||||
/**
|
||||
* Invert the matrix
|
||||
* @method
|
||||
* @name Konva.Transform#invert
|
||||
* @returns {Konva.Transform}
|
||||
*/
|
||||
isInvertible(): boolean;
|
||||
invert(): this;
|
||||
/**
|
||||
* return matrix
|
||||
* @method
|
||||
* @name Konva.Transform#getMatrix
|
||||
*/
|
||||
getMatrix(): number[];
|
||||
/**
|
||||
* convert transformation matrix back into node's attributes
|
||||
* @method
|
||||
* @name Konva.Transform#decompose
|
||||
* @returns {Konva.Transform}
|
||||
*/
|
||||
decompose(): {
|
||||
x: number;
|
||||
y: number;
|
||||
rotation: number;
|
||||
scaleX: number;
|
||||
scaleY: number;
|
||||
skewX: number;
|
||||
skewY: number;
|
||||
};
|
||||
}
|
||||
export declare const TypedArray: any;
|
||||
export declare const Util: {
|
||||
_isElement(obj: any): obj is Element;
|
||||
_isFunction(obj: any): boolean;
|
||||
_isPlainObject(obj: any): boolean;
|
||||
_isArray(obj: any): obj is Array<any>;
|
||||
_isNumber(obj: any): obj is number;
|
||||
_isString(obj: any): obj is string;
|
||||
_isBoolean(obj: any): obj is boolean;
|
||||
isObject(val: any): val is object;
|
||||
isValidSelector(selector: any): boolean;
|
||||
_sign(number: number): 1 | -1;
|
||||
requestAnimFrame(callback: Function, win?: Window | null): void;
|
||||
createCanvasElement(): HTMLCanvasElement;
|
||||
createImageElement(): HTMLImageElement;
|
||||
_urlToImage(url: string, callback: Function, onError?: (error: Error) => void): void;
|
||||
_rgbToHex(r: number, g: number, b: number): string;
|
||||
_hexToRgb(hex: string): RGB;
|
||||
/**
|
||||
* return random hex color
|
||||
* @method
|
||||
* @memberof Konva.Util
|
||||
* @example
|
||||
* shape.fill(Konva.Util.getRandomColor());
|
||||
*/
|
||||
getRandomColor(): string;
|
||||
/**
|
||||
* Check if canvas farbling is active (e.g., Brave browser fingerprinting protection)
|
||||
* @method
|
||||
* @memberof Konva.Util
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
isCanvasFarblingActive(): boolean;
|
||||
/**
|
||||
* Get a random color for hit detection (snapped to the hit color grid)
|
||||
* @method
|
||||
* @memberof Konva.Util
|
||||
* @returns {String} hex color string
|
||||
*/
|
||||
getHitColor(): string;
|
||||
/**
|
||||
* Get hit color key from RGB values (snapped to the hit color grid)
|
||||
* @method
|
||||
* @memberof Konva.Util
|
||||
* @param {Number} r - red component (0-255)
|
||||
* @param {Number} g - green component (0-255)
|
||||
* @param {Number} b - blue component (0-255)
|
||||
* @returns {String} hex color key string
|
||||
*/
|
||||
getHitColorKey(r: number, g: number, b: number): string;
|
||||
/**
|
||||
* Snap a hex color to the hit color grid
|
||||
* @method
|
||||
* @memberof Konva.Util
|
||||
* @param {String} hex - hex color string (e.g., "#ff00ff")
|
||||
* @returns {String} snapped hex color string
|
||||
*/
|
||||
getSnappedHexColor(hex: string): string;
|
||||
/**
|
||||
* get RGB components of a color
|
||||
* @method
|
||||
* @memberof Konva.Util
|
||||
* @param {String} color
|
||||
* @example
|
||||
* // each of the following examples return {r:0, g:0, b:255}
|
||||
* var rgb = Konva.Util.getRGB('blue');
|
||||
* var rgb = Konva.Util.getRGB('#0000ff');
|
||||
* var rgb = Konva.Util.getRGB('rgb(0,0,255)');
|
||||
*/
|
||||
getRGB(color: string): RGB;
|
||||
colorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_namedColorToRBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | null;
|
||||
_rgbColorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_hex8ColorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_hex6ColorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_hex4ColorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_hex3ColorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_hslColorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_getPointsRect(points: ArrayLike<number>): IRect;
|
||||
/**
|
||||
* check intersection of two client rectangles
|
||||
* @method
|
||||
* @memberof Konva.Util
|
||||
* @param {Object} r1 - { x, y, width, height } client rectangle
|
||||
* @param {Object} r2 - { x, y, width, height } client rectangle
|
||||
* @example
|
||||
* const overlapping = Konva.Util.haveIntersection(shape1.getClientRect(), shape2.getClientRect());
|
||||
*/
|
||||
haveIntersection(r1: IRect, r2: IRect): boolean;
|
||||
cloneObject<Any>(obj: Any): Any;
|
||||
_cloneValue(val: any): any;
|
||||
cloneArray(arr: Array<any>): any[];
|
||||
degToRad(deg: number): number;
|
||||
radToDeg(rad: number): number;
|
||||
_getRotation(radians: number): number;
|
||||
_capitalize(str: string): string;
|
||||
throw(str: string): never;
|
||||
error(str: string): void;
|
||||
warn(str: string): void;
|
||||
_batchEvents(batch: ((run: () => void) => void) | undefined, run: () => void): void;
|
||||
each(obj: object, func: Function): void;
|
||||
_inRange(val: number, left: number, right: number): boolean;
|
||||
_getProjectionToSegment(x1: any, y1: any, x2: any, y2: any, x3: any, y3: any): any[];
|
||||
_getProjectionToLine(pt: Vector2d, line: Array<Vector2d>, isClosed: boolean): Vector2d;
|
||||
_prepareArrayForTween(startArray: any, endArray: any, isClosed: any): number[];
|
||||
_prepareToStringify<T>(obj: any, ancestors?: Set<object>): T;
|
||||
_assign<T, U>(target: T, source: U): T & U;
|
||||
_getEventType(type: string): "mouse" | "touch" | "pointer";
|
||||
_getFirstPointerId(evt: any): any;
|
||||
releaseCanvas(...canvases: HTMLCanvasElement[]): void;
|
||||
_cornerRadii(cornerRadius: number | number[], width: number, height: number): [number, number, number, number];
|
||||
drawRoundedRectPath(context: Context, width: number, height: number, cornerRadius: number | number[]): void;
|
||||
drawRoundedPolygonPath(context: Context, points: Vector2d[], sides: number, radius: number, cornerRadius: number | number[]): void;
|
||||
};
|
||||
export type AnyString<T> = T | (string & {});
|
||||
+1220
File diff suppressed because it is too large
Load Diff
+9
@@ -0,0 +1,9 @@
|
||||
export declare function RGBComponent(val: number): number;
|
||||
export declare function getNumberValidator<T>(): ((val: T, attr: string) => T) | undefined;
|
||||
export declare function getNumberOrArrayOfNumbersValidator<T>(noOfElements: number): ((val: T, attr: string) => T) | undefined;
|
||||
export declare function getNumberOrAutoValidator<T>(): ((val: T, attr: string) => T) | undefined;
|
||||
export declare function getStringValidator<T>(): ((val: T, attr: string) => T) | undefined;
|
||||
export declare function getStringOrGradientValidator<T>(): ((val: T, attr: string) => T) | undefined;
|
||||
export declare function getNumberArrayValidator<T>(): ((val: T, attr: string) => T) | undefined;
|
||||
export declare function getBooleanValidator<T>(): ((val: T, attr: string) => T) | undefined;
|
||||
export declare function getComponentValidator<T>(components: string[]): ((val: T, attr: string) => T) | undefined;
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
import { Konva } from "./Global.js";
|
||||
import { TypedArray, Util } from "./Util.js";
|
||||
function _formatValue(val) {
|
||||
if (Util._isString(val)) {
|
||||
return '"' + val + '"';
|
||||
}
|
||||
if (Object.prototype.toString.call(val) === '[object Number]') {
|
||||
return val;
|
||||
}
|
||||
if (Util._isBoolean(val)) {
|
||||
return val;
|
||||
}
|
||||
return Object.prototype.toString.call(val);
|
||||
}
|
||||
export function RGBComponent(val) {
|
||||
if (val > 255) {
|
||||
return 255;
|
||||
}
|
||||
else if (val < 0) {
|
||||
return 0;
|
||||
}
|
||||
return Math.round(val);
|
||||
}
|
||||
export function getNumberValidator() {
|
||||
if (Konva.isUnminified) {
|
||||
return function (val, attr) {
|
||||
if (!Util._isNumber(val)) {
|
||||
Util.warn(_formatValue(val) +
|
||||
' is a not valid value for "' +
|
||||
attr +
|
||||
'" attribute. The value should be a number.');
|
||||
}
|
||||
return val;
|
||||
};
|
||||
}
|
||||
}
|
||||
export function getNumberOrArrayOfNumbersValidator(noOfElements) {
|
||||
if (Konva.isUnminified) {
|
||||
return function (val, attr) {
|
||||
let isNumber = Util._isNumber(val);
|
||||
let isValidArray = Util._isArray(val) && val.length == noOfElements;
|
||||
if (!isNumber && !isValidArray) {
|
||||
Util.warn(_formatValue(val) +
|
||||
' is a not valid value for "' +
|
||||
attr +
|
||||
'" attribute. The value should be a number or Array<number>(' +
|
||||
noOfElements +
|
||||
')');
|
||||
}
|
||||
return val;
|
||||
};
|
||||
}
|
||||
}
|
||||
export function getNumberOrAutoValidator() {
|
||||
if (Konva.isUnminified) {
|
||||
return function (val, attr) {
|
||||
const isNumber = Util._isNumber(val);
|
||||
const isAuto = val === 'auto';
|
||||
if (!(isNumber || isAuto)) {
|
||||
Util.warn(_formatValue(val) +
|
||||
' is a not valid value for "' +
|
||||
attr +
|
||||
'" attribute. The value should be a number or "auto".');
|
||||
}
|
||||
return val;
|
||||
};
|
||||
}
|
||||
}
|
||||
export function getStringValidator() {
|
||||
if (Konva.isUnminified) {
|
||||
return function (val, attr) {
|
||||
if (!Util._isString(val)) {
|
||||
Util.warn(_formatValue(val) +
|
||||
' is a not valid value for "' +
|
||||
attr +
|
||||
'" attribute. The value should be a string.');
|
||||
}
|
||||
return val;
|
||||
};
|
||||
}
|
||||
}
|
||||
export function getStringOrGradientValidator() {
|
||||
if (Konva.isUnminified) {
|
||||
return function (val, attr) {
|
||||
const isString = Util._isString(val);
|
||||
const isGradient = Object.prototype.toString.call(val) === '[object CanvasGradient]' ||
|
||||
(val && val['addColorStop']);
|
||||
if (!(isString || isGradient)) {
|
||||
Util.warn(_formatValue(val) +
|
||||
' is a not valid value for "' +
|
||||
attr +
|
||||
'" attribute. The value should be a string or a native gradient.');
|
||||
}
|
||||
return val;
|
||||
};
|
||||
}
|
||||
}
|
||||
export function getNumberArrayValidator() {
|
||||
if (Konva.isUnminified) {
|
||||
return function (val, attr) {
|
||||
if (val instanceof TypedArray) {
|
||||
return val;
|
||||
}
|
||||
if (!Util._isArray(val)) {
|
||||
Util.warn(_formatValue(val) +
|
||||
' is a not valid value for "' +
|
||||
attr +
|
||||
'" attribute. The value should be a array of numbers.');
|
||||
}
|
||||
else {
|
||||
val.forEach(function (item) {
|
||||
if (!Util._isNumber(item)) {
|
||||
Util.warn('"' +
|
||||
attr +
|
||||
'" attribute has non numeric element ' +
|
||||
item +
|
||||
'. Make sure that all elements are numbers.');
|
||||
}
|
||||
});
|
||||
}
|
||||
return val;
|
||||
};
|
||||
}
|
||||
}
|
||||
export function getBooleanValidator() {
|
||||
if (Konva.isUnminified) {
|
||||
return function (val, attr) {
|
||||
const isBool = val === true || val === false;
|
||||
if (!isBool) {
|
||||
Util.warn(_formatValue(val) +
|
||||
' is a not valid value for "' +
|
||||
attr +
|
||||
'" attribute. The value should be a boolean.');
|
||||
}
|
||||
return val;
|
||||
};
|
||||
}
|
||||
}
|
||||
export function getComponentValidator(components) {
|
||||
if (Konva.isUnminified) {
|
||||
return function (val, attr) {
|
||||
// ignore validation on undefined value, because it will reset to defalt
|
||||
if (val === undefined || val === null) {
|
||||
return val;
|
||||
}
|
||||
if (!Util.isObject(val)) {
|
||||
Util.warn(_formatValue(val) +
|
||||
' is a not valid value for "' +
|
||||
attr +
|
||||
'" attribute. The value should be an object with properties ' +
|
||||
components);
|
||||
}
|
||||
return val;
|
||||
};
|
||||
}
|
||||
}
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
import { Transform } from './Util.ts';
|
||||
import { Node } from './Node.ts';
|
||||
import { Container } from './Container.ts';
|
||||
import { Stage } from './Stage.ts';
|
||||
import { Layer } from './Layer.ts';
|
||||
import { FastLayer } from './FastLayer.ts';
|
||||
import { Group } from './Group.ts';
|
||||
import { Shape } from './Shape.ts';
|
||||
import { Animation } from './Animation.ts';
|
||||
import { Tween } from './Tween.ts';
|
||||
import { Context } from './Context.ts';
|
||||
import { Canvas } from './Canvas.ts';
|
||||
export declare const Konva: {
|
||||
_global: any;
|
||||
version: string;
|
||||
isBrowser: boolean;
|
||||
isUnminified: boolean;
|
||||
dblClickWindow: number;
|
||||
getAngle(angle: number): number;
|
||||
enableTrace: boolean;
|
||||
pointerEventsEnabled: boolean;
|
||||
autoDrawEnabled: boolean;
|
||||
hitOnDragEnabled: boolean;
|
||||
capturePointerEventsEnabled: boolean;
|
||||
_renderBackend: string;
|
||||
legacyTextRendering: boolean;
|
||||
pixelRatio: number;
|
||||
dragDistance: number;
|
||||
angleDeg: boolean;
|
||||
showWarnings: boolean;
|
||||
dragButtons: number[];
|
||||
isDragging(): boolean;
|
||||
isTransforming(): boolean;
|
||||
isDragReady(): boolean;
|
||||
releaseCanvasOnDestroy: boolean;
|
||||
document: any;
|
||||
_injectGlobal(Konva: any): void;
|
||||
} & {
|
||||
/**
|
||||
* Defer automatic Konva.Transformer updates across many synchronous node changes
|
||||
* and run them once when the returned function is called.
|
||||
* A Transformer normally updates after every change of an attached node, so a loop
|
||||
* over N selected nodes costs N updates, each measuring all N nodes. In a batch it costs one.
|
||||
* A single setAttrs(), position(), destroyChildren(), removeChildren() call or animation frame is
|
||||
* already batched; use this for anything else, for example a React commit or an undo.
|
||||
* Attributes and change events stay synchronous. Inside the batch, the Transformer's own
|
||||
* x(), y(), width(), height() and getClientRect() are computed on read; its anchors are laid out
|
||||
* when the batch finishes, or earlier if something draws or measures them.
|
||||
* Change events fire inside the batch, so a listener sees the new attributes and the
|
||||
* Transformer's box, but not its anchors; call transformer.update() there if it needs them.
|
||||
* Batches nest. Call the returned function in a finally block, and do not keep a batch
|
||||
* open across an await or an animation frame.
|
||||
* @method
|
||||
* @name Konva.beginUpdateBatch
|
||||
* @returns {Function} finishes the batch and runs the pending updates
|
||||
* @example
|
||||
* const finish = Konva.beginUpdateBatch();
|
||||
* try {
|
||||
* selectedNodes.forEach((node) => node.width(100));
|
||||
* } finally {
|
||||
* finish();
|
||||
* }
|
||||
*/
|
||||
beginUpdateBatch: typeof Node._beginUpdateBatch;
|
||||
Util: {
|
||||
_isElement(obj: any): obj is Element;
|
||||
_isFunction(obj: any): boolean;
|
||||
_isPlainObject(obj: any): boolean;
|
||||
_isArray(obj: any): obj is Array<any>;
|
||||
_isNumber(obj: any): obj is number;
|
||||
_isString(obj: any): obj is string;
|
||||
_isBoolean(obj: any): obj is boolean;
|
||||
isObject(val: any): val is object;
|
||||
isValidSelector(selector: any): boolean;
|
||||
_sign(number: number): 1 | -1;
|
||||
requestAnimFrame(callback: Function, win?: Window | null): void;
|
||||
createCanvasElement(): HTMLCanvasElement;
|
||||
createImageElement(): HTMLImageElement;
|
||||
_urlToImage(url: string, callback: Function, onError?: (error: Error) => void): void;
|
||||
_rgbToHex(r: number, g: number, b: number): string;
|
||||
_hexToRgb(hex: string): import("./types.ts").RGB;
|
||||
getRandomColor(): string;
|
||||
isCanvasFarblingActive(): boolean;
|
||||
getHitColor(): string;
|
||||
getHitColorKey(r: number, g: number, b: number): string;
|
||||
getSnappedHexColor(hex: string): string;
|
||||
getRGB(color: string): import("./types.ts").RGB;
|
||||
colorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_namedColorToRBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | null;
|
||||
_rgbColorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_hex8ColorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_hex6ColorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_hex4ColorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_hex3ColorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_hslColorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_getPointsRect(points: ArrayLike<number>): import("./types.ts").IRect;
|
||||
haveIntersection(r1: import("./types.ts").IRect, r2: import("./types.ts").IRect): boolean;
|
||||
cloneObject<Any>(obj: Any): Any;
|
||||
_cloneValue(val: any): any;
|
||||
cloneArray(arr: Array<any>): any[];
|
||||
degToRad(deg: number): number;
|
||||
radToDeg(rad: number): number;
|
||||
_getRotation(radians: number): number;
|
||||
_capitalize(str: string): string;
|
||||
throw(str: string): never;
|
||||
error(str: string): void;
|
||||
warn(str: string): void;
|
||||
_batchEvents(batch: ((run: () => void) => void) | undefined, run: () => void): void;
|
||||
each(obj: object, func: Function): void;
|
||||
_inRange(val: number, left: number, right: number): boolean;
|
||||
_getProjectionToSegment(x1: any, y1: any, x2: any, y2: any, x3: any, y3: any): any[];
|
||||
_getProjectionToLine(pt: import("./types.ts").Vector2d, line: Array<import("./types.ts").Vector2d>, isClosed: boolean): import("./types.ts").Vector2d;
|
||||
_prepareArrayForTween(startArray: any, endArray: any, isClosed: any): number[];
|
||||
_prepareToStringify<T>(obj: any, ancestors?: Set<object>): T;
|
||||
_assign<T, U>(target: T, source: U): T & U;
|
||||
_getEventType(type: string): "mouse" | "touch" | "pointer";
|
||||
_getFirstPointerId(evt: any): any;
|
||||
releaseCanvas(...canvases: HTMLCanvasElement[]): void;
|
||||
_cornerRadii(cornerRadius: number | number[], width: number, height: number): [number, number, number, number];
|
||||
drawRoundedRectPath(context: Context, width: number, height: number, cornerRadius: number | number[]): void;
|
||||
drawRoundedPolygonPath(context: Context, points: import("./types.ts").Vector2d[], sides: number, radius: number, cornerRadius: number | number[]): void;
|
||||
};
|
||||
Transform: typeof Transform;
|
||||
Node: typeof Node;
|
||||
Container: typeof Container;
|
||||
Stage: typeof Stage;
|
||||
stages: Stage[];
|
||||
Layer: typeof Layer;
|
||||
FastLayer: typeof FastLayer;
|
||||
Group: typeof Group;
|
||||
DD: {
|
||||
readonly isDragging: boolean;
|
||||
readonly node: Node<import("./Node.ts").NodeConfig> | undefined;
|
||||
_dragElements: Map<number, {
|
||||
node: Node;
|
||||
startPointerPos: import("./types.ts").Vector2d;
|
||||
offset: import("./types.ts").Vector2d;
|
||||
pointerId?: number;
|
||||
pointerEventType?: Stage["_pointerEventType"];
|
||||
lastPos?: import("./types.ts").Vector2d;
|
||||
startEvent?: any;
|
||||
dragStatus: "ready" | "dragging" | "stopped";
|
||||
}>;
|
||||
_listenToWindow(win: Window): void;
|
||||
_batchEvents(callback: () => void, win?: Window): void;
|
||||
_drag(evt: any, win?: Window): void;
|
||||
_endDragBefore(evt?: any, win?: Window, only?: {
|
||||
node: Node;
|
||||
startPointerPos: import("./types.ts").Vector2d;
|
||||
offset: import("./types.ts").Vector2d;
|
||||
pointerId?: number;
|
||||
pointerEventType?: Stage["_pointerEventType"];
|
||||
lastPos?: import("./types.ts").Vector2d;
|
||||
startEvent?: any;
|
||||
dragStatus: "ready" | "dragging" | "stopped";
|
||||
}): void;
|
||||
_endDragAfter(evt: any, only?: {
|
||||
node: Node;
|
||||
startPointerPos: import("./types.ts").Vector2d;
|
||||
offset: import("./types.ts").Vector2d;
|
||||
pointerId?: number;
|
||||
pointerEventType?: Stage["_pointerEventType"];
|
||||
lastPos?: import("./types.ts").Vector2d;
|
||||
startEvent?: any;
|
||||
dragStatus: "ready" | "dragging" | "stopped";
|
||||
}): void;
|
||||
};
|
||||
Shape: typeof Shape;
|
||||
shapes: {
|
||||
[key: string]: Shape<import("./Shape.ts").ShapeConfig>;
|
||||
};
|
||||
Animation: typeof Animation;
|
||||
Tween: typeof Tween;
|
||||
Easings: {
|
||||
BackEaseIn(t: any, b: any, c: any, d: any): any;
|
||||
BackEaseOut(t: any, b: any, c: any, d: any): any;
|
||||
BackEaseInOut(t: any, b: any, c: any, d: any): any;
|
||||
ElasticEaseIn(t: any, b: any, c: any, d: any, a: any, p: any): any;
|
||||
ElasticEaseOut(t: any, b: any, c: any, d: any, a: any, p: any): any;
|
||||
ElasticEaseInOut(t: any, b: any, c: any, d: any, a: any, p: any): any;
|
||||
BounceEaseOut(t: any, b: any, c: any, d: any): any;
|
||||
BounceEaseIn(t: any, b: any, c: any, d: any): any;
|
||||
BounceEaseInOut(t: any, b: any, c: any, d: any): any;
|
||||
EaseIn(t: any, b: any, c: any, d: any): any;
|
||||
EaseOut(t: any, b: any, c: any, d: any): any;
|
||||
EaseInOut(t: any, b: any, c: any, d: any): any;
|
||||
StrongEaseIn(t: any, b: any, c: any, d: any): any;
|
||||
StrongEaseOut(t: any, b: any, c: any, d: any): any;
|
||||
StrongEaseInOut(t: any, b: any, c: any, d: any): any;
|
||||
Linear(t: any, b: any, c: any, d: any): any;
|
||||
};
|
||||
Context: typeof Context;
|
||||
Canvas: typeof Canvas;
|
||||
};
|
||||
export declare namespace Konva {
|
||||
type Vector2d = import('./types.ts').Vector2d;
|
||||
type IRect = import('./types.ts').IRect;
|
||||
type GetSet<Type, This> = import('./types.ts').GetSet<Type, This>;
|
||||
type Filter = import('./Node.ts').Filter;
|
||||
type LineCap = import('./Shape.ts').LineCap;
|
||||
type LineJoin = import('./Shape.ts').LineJoin;
|
||||
type Node = import('./Node.ts').Node;
|
||||
type NodeConfig = import('./Node.ts').NodeConfig;
|
||||
type KonvaEventObject<EventType> = import('./Node.ts').KonvaEventObject<EventType>;
|
||||
type KonvaPointerEvent = import('./PointerEvents.ts').KonvaPointerEvent;
|
||||
type KonvaEventListener<This, EventType> = import('./Node.ts').KonvaEventListener<This, EventType>;
|
||||
type Container = import('./Container.ts').Container<Node>;
|
||||
type ContainerConfig = import('./Container.ts').ContainerConfig;
|
||||
type Transform = import('./Util.ts').Transform;
|
||||
type Context = import('./Context.ts').Context;
|
||||
type Stage = import('./Stage.ts').Stage;
|
||||
type StageConfig = import('./Stage.ts').StageConfig;
|
||||
type Layer = import('./Layer.ts').Layer;
|
||||
type LayerConfig = import('./Layer.ts').LayerConfig;
|
||||
type FastLayer = import('./FastLayer.ts').FastLayer;
|
||||
type Group = import('./Group.ts').Group;
|
||||
type GroupConfig = import('./Group.ts').GroupConfig;
|
||||
type Shape = import('./Shape.ts').Shape;
|
||||
type ShapeConfig = import('./Shape.ts').ShapeConfig;
|
||||
type Animation = import('./Animation.ts').Animation;
|
||||
type Tween = import('./Tween.ts').Tween;
|
||||
type TweenConfig = import('./Tween.ts').TweenConfig;
|
||||
}
|
||||
export default Konva;
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
// what is core parts of Konva?
|
||||
import { Konva as Global } from "./Global.js";
|
||||
import { Util, Transform } from "./Util.js";
|
||||
import { Node } from "./Node.js";
|
||||
import { Container } from "./Container.js";
|
||||
import { Stage, stages } from "./Stage.js";
|
||||
import { Layer } from "./Layer.js";
|
||||
import { FastLayer } from "./FastLayer.js";
|
||||
import { Group } from "./Group.js";
|
||||
import { DD } from "./DragAndDrop.js";
|
||||
import { Shape, shapes } from "./Shape.js";
|
||||
import { Animation } from "./Animation.js";
|
||||
import { Tween, Easings } from "./Tween.js";
|
||||
import { Context } from "./Context.js";
|
||||
import { Canvas } from "./Canvas.js";
|
||||
export const Konva = Util._assign(Global, {
|
||||
/**
|
||||
* Defer automatic Konva.Transformer updates across many synchronous node changes
|
||||
* and run them once when the returned function is called.
|
||||
* A Transformer normally updates after every change of an attached node, so a loop
|
||||
* over N selected nodes costs N updates, each measuring all N nodes. In a batch it costs one.
|
||||
* A single setAttrs(), position(), destroyChildren(), removeChildren() call or animation frame is
|
||||
* already batched; use this for anything else, for example a React commit or an undo.
|
||||
* Attributes and change events stay synchronous. Inside the batch, the Transformer's own
|
||||
* x(), y(), width(), height() and getClientRect() are computed on read; its anchors are laid out
|
||||
* when the batch finishes, or earlier if something draws or measures them.
|
||||
* Change events fire inside the batch, so a listener sees the new attributes and the
|
||||
* Transformer's box, but not its anchors; call transformer.update() there if it needs them.
|
||||
* Batches nest. Call the returned function in a finally block, and do not keep a batch
|
||||
* open across an await or an animation frame.
|
||||
* @method
|
||||
* @name Konva.beginUpdateBatch
|
||||
* @returns {Function} finishes the batch and runs the pending updates
|
||||
* @example
|
||||
* const finish = Konva.beginUpdateBatch();
|
||||
* try {
|
||||
* selectedNodes.forEach((node) => node.width(100));
|
||||
* } finally {
|
||||
* finish();
|
||||
* }
|
||||
*/
|
||||
beginUpdateBatch: Node._beginUpdateBatch,
|
||||
Util,
|
||||
Transform,
|
||||
Node,
|
||||
Container,
|
||||
Stage,
|
||||
stages,
|
||||
Layer,
|
||||
FastLayer,
|
||||
Group,
|
||||
DD,
|
||||
Shape,
|
||||
shapes,
|
||||
Animation,
|
||||
Tween,
|
||||
Easings,
|
||||
Context,
|
||||
Canvas,
|
||||
});
|
||||
export default Konva;
|
||||
+326
@@ -0,0 +1,326 @@
|
||||
import { Konva as Core } from './_CoreInternals.ts';
|
||||
import { Arc } from './shapes/Arc.ts';
|
||||
import { Arrow } from './shapes/Arrow.ts';
|
||||
import { Circle } from './shapes/Circle.ts';
|
||||
import { Ellipse } from './shapes/Ellipse.ts';
|
||||
import { Image } from './shapes/Image.ts';
|
||||
import { Label, Tag } from './shapes/Label.ts';
|
||||
import { Line } from './shapes/Line.ts';
|
||||
import { Path } from './shapes/Path.ts';
|
||||
import { Rect } from './shapes/Rect.ts';
|
||||
import { RegularPolygon } from './shapes/RegularPolygon.ts';
|
||||
import { Ring } from './shapes/Ring.ts';
|
||||
import { Sprite } from './shapes/Sprite.ts';
|
||||
import { Star } from './shapes/Star.ts';
|
||||
import { Text } from './shapes/Text.ts';
|
||||
import { TextPath } from './shapes/TextPath.ts';
|
||||
import { Transformer } from './shapes/Transformer.ts';
|
||||
import { Wedge } from './shapes/Wedge.ts';
|
||||
export declare const Konva: {
|
||||
_global: any;
|
||||
version: string;
|
||||
isBrowser: boolean;
|
||||
isUnminified: boolean;
|
||||
dblClickWindow: number;
|
||||
getAngle(angle: number): number;
|
||||
enableTrace: boolean;
|
||||
pointerEventsEnabled: boolean;
|
||||
autoDrawEnabled: boolean;
|
||||
hitOnDragEnabled: boolean;
|
||||
capturePointerEventsEnabled: boolean;
|
||||
_renderBackend: string;
|
||||
legacyTextRendering: boolean;
|
||||
pixelRatio: number;
|
||||
dragDistance: number;
|
||||
angleDeg: boolean;
|
||||
showWarnings: boolean;
|
||||
dragButtons: number[];
|
||||
isDragging(): boolean;
|
||||
isTransforming(): boolean;
|
||||
isDragReady(): boolean;
|
||||
releaseCanvasOnDestroy: boolean;
|
||||
document: any;
|
||||
_injectGlobal(Konva: any): void;
|
||||
} & {
|
||||
beginUpdateBatch: typeof import("./Node.ts").Node._beginUpdateBatch;
|
||||
Util: {
|
||||
_isElement(obj: any): obj is Element;
|
||||
_isFunction(obj: any): boolean;
|
||||
_isPlainObject(obj: any): boolean;
|
||||
_isArray(obj: any): obj is Array<any>;
|
||||
_isNumber(obj: any): obj is number;
|
||||
_isString(obj: any): obj is string;
|
||||
_isBoolean(obj: any): obj is boolean;
|
||||
isObject(val: any): val is object;
|
||||
isValidSelector(selector: any): boolean;
|
||||
_sign(number: number): 1 | -1;
|
||||
requestAnimFrame(callback: Function, win?: Window | null): void;
|
||||
createCanvasElement(): HTMLCanvasElement;
|
||||
createImageElement(): HTMLImageElement;
|
||||
_urlToImage(url: string, callback: Function, onError?: (error: Error) => void): void;
|
||||
_rgbToHex(r: number, g: number, b: number): string;
|
||||
_hexToRgb(hex: string): import("./types.ts").RGB;
|
||||
getRandomColor(): string;
|
||||
isCanvasFarblingActive(): boolean;
|
||||
getHitColor(): string;
|
||||
getHitColorKey(r: number, g: number, b: number): string;
|
||||
getSnappedHexColor(hex: string): string;
|
||||
getRGB(color: string): import("./types.ts").RGB;
|
||||
colorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_namedColorToRBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | null;
|
||||
_rgbColorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_hex8ColorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_hex6ColorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_hex4ColorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_hex3ColorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_hslColorToRGBA(str: string): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
} | undefined;
|
||||
_getPointsRect(points: ArrayLike<number>): import("./types.ts").IRect;
|
||||
haveIntersection(r1: import("./types.ts").IRect, r2: import("./types.ts").IRect): boolean;
|
||||
cloneObject<Any>(obj: Any): Any;
|
||||
_cloneValue(val: any): any;
|
||||
cloneArray(arr: Array<any>): any[];
|
||||
degToRad(deg: number): number;
|
||||
radToDeg(rad: number): number;
|
||||
_getRotation(radians: number): number;
|
||||
_capitalize(str: string): string;
|
||||
throw(str: string): never;
|
||||
error(str: string): void;
|
||||
warn(str: string): void;
|
||||
_batchEvents(batch: ((run: () => void) => void) | undefined, run: () => void): void;
|
||||
each(obj: object, func: Function): void;
|
||||
_inRange(val: number, left: number, right: number): boolean;
|
||||
_getProjectionToSegment(x1: any, y1: any, x2: any, y2: any, x3: any, y3: any): any[];
|
||||
_getProjectionToLine(pt: import("./types.ts").Vector2d, line: Array<import("./types.ts").Vector2d>, isClosed: boolean): import("./types.ts").Vector2d;
|
||||
_prepareArrayForTween(startArray: any, endArray: any, isClosed: any): number[];
|
||||
_prepareToStringify<T>(obj: any, ancestors?: Set<object>): T;
|
||||
_assign<T, U>(target: T, source: U): T & U;
|
||||
_getEventType(type: string): "mouse" | "touch" | "pointer";
|
||||
_getFirstPointerId(evt: any): any;
|
||||
releaseCanvas(...canvases: HTMLCanvasElement[]): void;
|
||||
_cornerRadii(cornerRadius: number | number[], width: number, height: number): [number, number, number, number];
|
||||
drawRoundedRectPath(context: import("./Context.ts").Context, width: number, height: number, cornerRadius: number | number[]): void;
|
||||
drawRoundedPolygonPath(context: import("./Context.ts").Context, points: import("./types.ts").Vector2d[], sides: number, radius: number, cornerRadius: number | number[]): void;
|
||||
};
|
||||
Transform: typeof import("./Util.ts").Transform;
|
||||
Node: typeof import("./Node.ts").Node;
|
||||
Container: typeof import("./Container.ts").Container;
|
||||
Stage: typeof import("./Stage.ts").Stage;
|
||||
stages: import("./Stage.ts").Stage[];
|
||||
Layer: typeof import("./Layer.ts").Layer;
|
||||
FastLayer: typeof import("./FastLayer.ts").FastLayer;
|
||||
Group: typeof import("./Group.ts").Group;
|
||||
DD: {
|
||||
readonly isDragging: boolean;
|
||||
readonly node: import("./Node.ts").Node<import("./Node.ts").NodeConfig> | undefined;
|
||||
_dragElements: Map<number, {
|
||||
node: import("./Node.ts").Node;
|
||||
startPointerPos: import("./types.ts").Vector2d;
|
||||
offset: import("./types.ts").Vector2d;
|
||||
pointerId?: number;
|
||||
pointerEventType?: import("./Stage.ts").Stage["_pointerEventType"];
|
||||
lastPos?: import("./types.ts").Vector2d;
|
||||
startEvent?: any;
|
||||
dragStatus: "ready" | "dragging" | "stopped";
|
||||
}>;
|
||||
_listenToWindow(win: Window): void;
|
||||
_batchEvents(callback: () => void, win?: Window): void;
|
||||
_drag(evt: any, win?: Window): void;
|
||||
_endDragBefore(evt?: any, win?: Window, only?: {
|
||||
node: import("./Node.ts").Node;
|
||||
startPointerPos: import("./types.ts").Vector2d;
|
||||
offset: import("./types.ts").Vector2d;
|
||||
pointerId?: number;
|
||||
pointerEventType?: import("./Stage.ts").Stage["_pointerEventType"];
|
||||
lastPos?: import("./types.ts").Vector2d;
|
||||
startEvent?: any;
|
||||
dragStatus: "ready" | "dragging" | "stopped";
|
||||
}): void;
|
||||
_endDragAfter(evt: any, only?: {
|
||||
node: import("./Node.ts").Node;
|
||||
startPointerPos: import("./types.ts").Vector2d;
|
||||
offset: import("./types.ts").Vector2d;
|
||||
pointerId?: number;
|
||||
pointerEventType?: import("./Stage.ts").Stage["_pointerEventType"];
|
||||
lastPos?: import("./types.ts").Vector2d;
|
||||
startEvent?: any;
|
||||
dragStatus: "ready" | "dragging" | "stopped";
|
||||
}): void;
|
||||
};
|
||||
Shape: typeof import("./Shape.ts").Shape;
|
||||
shapes: {
|
||||
[key: string]: import("./Shape.ts").Shape<import("./Shape.ts").ShapeConfig>;
|
||||
};
|
||||
Animation: typeof import("./Animation.ts").Animation;
|
||||
Tween: typeof import("./Tween.ts").Tween;
|
||||
Easings: {
|
||||
BackEaseIn(t: any, b: any, c: any, d: any): any;
|
||||
BackEaseOut(t: any, b: any, c: any, d: any): any;
|
||||
BackEaseInOut(t: any, b: any, c: any, d: any): any;
|
||||
ElasticEaseIn(t: any, b: any, c: any, d: any, a: any, p: any): any;
|
||||
ElasticEaseOut(t: any, b: any, c: any, d: any, a: any, p: any): any;
|
||||
ElasticEaseInOut(t: any, b: any, c: any, d: any, a: any, p: any): any;
|
||||
BounceEaseOut(t: any, b: any, c: any, d: any): any;
|
||||
BounceEaseIn(t: any, b: any, c: any, d: any): any;
|
||||
BounceEaseInOut(t: any, b: any, c: any, d: any): any;
|
||||
EaseIn(t: any, b: any, c: any, d: any): any;
|
||||
EaseOut(t: any, b: any, c: any, d: any): any;
|
||||
EaseInOut(t: any, b: any, c: any, d: any): any;
|
||||
StrongEaseIn(t: any, b: any, c: any, d: any): any;
|
||||
StrongEaseOut(t: any, b: any, c: any, d: any): any;
|
||||
StrongEaseInOut(t: any, b: any, c: any, d: any): any;
|
||||
Linear(t: any, b: any, c: any, d: any): any;
|
||||
};
|
||||
Context: typeof import("./Context.ts").Context;
|
||||
Canvas: typeof import("./Canvas.ts").Canvas;
|
||||
} & {
|
||||
Arc: typeof Arc;
|
||||
Arrow: typeof Arrow;
|
||||
Circle: typeof Circle;
|
||||
Ellipse: typeof Ellipse;
|
||||
Image: typeof Image;
|
||||
Label: typeof Label;
|
||||
Tag: typeof Tag;
|
||||
Line: typeof Line;
|
||||
Path: typeof Path;
|
||||
Rect: typeof Rect;
|
||||
RegularPolygon: typeof RegularPolygon;
|
||||
Ring: typeof Ring;
|
||||
Sprite: typeof Sprite;
|
||||
Star: typeof Star;
|
||||
Text: typeof Text;
|
||||
TextPath: typeof TextPath;
|
||||
Transformer: typeof Transformer;
|
||||
Wedge: typeof Wedge;
|
||||
/**
|
||||
* @namespace Filters
|
||||
* @memberof Konva
|
||||
*/
|
||||
Filters: {
|
||||
Blur: import("./Node.ts").Filter;
|
||||
Brightness: import("./Node.ts").Filter;
|
||||
Brighten: import("./Node.ts").Filter;
|
||||
Contrast: import("./Node.ts").Filter;
|
||||
Emboss: import("./Node.ts").Filter;
|
||||
Enhance: import("./Node.ts").Filter;
|
||||
Grayscale: import("./Node.ts").Filter;
|
||||
HSL: import("./Node.ts").Filter;
|
||||
HSV: import("./Node.ts").Filter;
|
||||
Invert: import("./Node.ts").Filter;
|
||||
Kaleidoscope: import("./Node.ts").Filter;
|
||||
Mask: import("./Node.ts").Filter;
|
||||
Noise: import("./Node.ts").Filter;
|
||||
Pixelate: import("./Node.ts").Filter;
|
||||
Posterize: import("./Node.ts").Filter;
|
||||
RGB: import("./Node.ts").Filter;
|
||||
RGBA: import("./Node.ts").Filter;
|
||||
Sepia: import("./Node.ts").Filter;
|
||||
Solarize: import("./Node.ts").Filter;
|
||||
Threshold: import("./Node.ts").Filter;
|
||||
};
|
||||
};
|
||||
export declare namespace Konva {
|
||||
type Vector2d = Core.Vector2d;
|
||||
type IRect = Core.IRect;
|
||||
type GetSet<Type, This> = Core.GetSet<Type, This>;
|
||||
type Filter = Core.Filter;
|
||||
type LineCap = Core.LineCap;
|
||||
type LineJoin = Core.LineJoin;
|
||||
type Box = import('./shapes/Transformer.ts').Box;
|
||||
type Node = Core.Node;
|
||||
type NodeConfig = Core.NodeConfig;
|
||||
type KonvaEventObject<EventType> = Core.KonvaEventObject<EventType>;
|
||||
type KonvaPointerEvent = Core.KonvaPointerEvent;
|
||||
type KonvaEventListener<This, EventType> = Core.KonvaEventListener<This, EventType>;
|
||||
type Container = Core.Container;
|
||||
type ContainerConfig = Core.ContainerConfig;
|
||||
type Transform = Core.Transform;
|
||||
type Context = Core.Context;
|
||||
type Stage = Core.Stage;
|
||||
type StageConfig = Core.StageConfig;
|
||||
type Layer = Core.Layer;
|
||||
type LayerConfig = Core.LayerConfig;
|
||||
type FastLayer = Core.FastLayer;
|
||||
type Group = Core.Group;
|
||||
type GroupConfig = Core.GroupConfig;
|
||||
type Shape = Core.Shape;
|
||||
type ShapeConfig = Core.ShapeConfig;
|
||||
type Animation = Core.Animation;
|
||||
type Tween = Core.Tween;
|
||||
type TweenConfig = Core.TweenConfig;
|
||||
type Arc = import('./shapes/Arc.ts').Arc;
|
||||
type ArcConfig = import('./shapes/Arc.ts').ArcConfig;
|
||||
type Arrow = import('./shapes/Arrow.ts').Arrow;
|
||||
type ArrowConfig = import('./shapes/Arrow.ts').ArrowConfig;
|
||||
type Circle = import('./shapes/Circle.ts').Circle;
|
||||
type CircleConfig = import('./shapes/Circle.ts').CircleConfig;
|
||||
type Ellipse = import('./shapes/Ellipse.ts').Ellipse;
|
||||
type EllipseConfig = import('./shapes/Ellipse.ts').EllipseConfig;
|
||||
type Image = import('./shapes/Image.ts').Image;
|
||||
type ImageConfig = import('./shapes/Image.ts').ImageConfig;
|
||||
type Label = import('./shapes/Label.ts').Label;
|
||||
type LabelConfig = import('./shapes/Label.ts').LabelConfig;
|
||||
type Tag = import('./shapes/Label.ts').Tag;
|
||||
type TagConfig = import('./shapes/Label.ts').TagConfig;
|
||||
type Line = import('./shapes/Line.ts').Line;
|
||||
type LineConfig = import('./shapes/Line.ts').LineConfig;
|
||||
type Path = import('./shapes/Path.ts').Path;
|
||||
type PathConfig = import('./shapes/Path.ts').PathConfig;
|
||||
type Rect = import('./shapes/Rect.ts').Rect;
|
||||
type RectConfig = import('./shapes/Rect.ts').RectConfig;
|
||||
type RegularPolygon = import('./shapes/RegularPolygon.ts').RegularPolygon;
|
||||
type RegularPolygonConfig = import('./shapes/RegularPolygon.ts').RegularPolygonConfig;
|
||||
type Ring = import('./shapes/Ring.ts').Ring;
|
||||
type RingConfig = import('./shapes/Ring.ts').RingConfig;
|
||||
type Sprite = import('./shapes/Sprite.ts').Sprite;
|
||||
type SpriteConfig = import('./shapes/Sprite.ts').SpriteConfig;
|
||||
type Star = import('./shapes/Star.ts').Star;
|
||||
type StarConfig = import('./shapes/Star.ts').StarConfig;
|
||||
type Text = import('./shapes/Text.ts').Text;
|
||||
type TextConfig = import('./shapes/Text.ts').TextConfig;
|
||||
type TextPath = import('./shapes/TextPath.ts').TextPath;
|
||||
type TextPathConfig = import('./shapes/TextPath.ts').TextPathConfig;
|
||||
type Transformer = import('./shapes/Transformer.ts').Transformer;
|
||||
type TransformerConfig = import('./shapes/Transformer.ts').TransformerConfig;
|
||||
type Wedge = import('./shapes/Wedge.ts').Wedge;
|
||||
type WedgeConfig = import('./shapes/Wedge.ts').WedgeConfig;
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
// we need to import core of the Konva and then extend it with all additional objects
|
||||
import { Konva as Core } from "./_CoreInternals.js";
|
||||
// shapes
|
||||
import { Arc } from "./shapes/Arc.js";
|
||||
import { Arrow } from "./shapes/Arrow.js";
|
||||
import { Circle } from "./shapes/Circle.js";
|
||||
import { Ellipse } from "./shapes/Ellipse.js";
|
||||
import { Image } from "./shapes/Image.js";
|
||||
import { Label, Tag } from "./shapes/Label.js";
|
||||
import { Line } from "./shapes/Line.js";
|
||||
import { Path } from "./shapes/Path.js";
|
||||
import { Rect } from "./shapes/Rect.js";
|
||||
import { RegularPolygon } from "./shapes/RegularPolygon.js";
|
||||
import { Ring } from "./shapes/Ring.js";
|
||||
import { Sprite } from "./shapes/Sprite.js";
|
||||
import { Star } from "./shapes/Star.js";
|
||||
import { Text } from "./shapes/Text.js";
|
||||
import { TextPath } from "./shapes/TextPath.js";
|
||||
import { Transformer } from "./shapes/Transformer.js";
|
||||
import { Wedge } from "./shapes/Wedge.js";
|
||||
// filters
|
||||
import { Blur } from "./filters/Blur.js";
|
||||
import { Brighten } from "./filters/Brighten.js";
|
||||
import { Brightness } from "./filters/Brightness.js";
|
||||
import { Contrast } from "./filters/Contrast.js";
|
||||
import { Emboss } from "./filters/Emboss.js";
|
||||
import { Enhance } from "./filters/Enhance.js";
|
||||
import { Grayscale } from "./filters/Grayscale.js";
|
||||
import { HSL } from "./filters/HSL.js";
|
||||
import { HSV } from "./filters/HSV.js";
|
||||
import { Invert } from "./filters/Invert.js";
|
||||
import { Kaleidoscope } from "./filters/Kaleidoscope.js";
|
||||
import { Mask } from "./filters/Mask.js";
|
||||
import { Noise } from "./filters/Noise.js";
|
||||
import { Pixelate } from "./filters/Pixelate.js";
|
||||
import { Posterize } from "./filters/Posterize.js";
|
||||
import { RGB } from "./filters/RGB.js";
|
||||
import { RGBA } from "./filters/RGBA.js";
|
||||
import { Sepia } from "./filters/Sepia.js";
|
||||
import { Solarize } from "./filters/Solarize.js";
|
||||
import { Threshold } from "./filters/Threshold.js";
|
||||
export const Konva = Core.Util._assign(Core, {
|
||||
Arc,
|
||||
Arrow,
|
||||
Circle,
|
||||
Ellipse,
|
||||
Image,
|
||||
Label,
|
||||
Tag,
|
||||
Line,
|
||||
Path,
|
||||
Rect,
|
||||
RegularPolygon,
|
||||
Ring,
|
||||
Sprite,
|
||||
Star,
|
||||
Text,
|
||||
TextPath,
|
||||
Transformer,
|
||||
Wedge,
|
||||
/**
|
||||
* @namespace Filters
|
||||
* @memberof Konva
|
||||
*/
|
||||
Filters: {
|
||||
Blur,
|
||||
Brightness,
|
||||
Brighten,
|
||||
Contrast,
|
||||
Emboss,
|
||||
Enhance,
|
||||
Grayscale,
|
||||
HSL,
|
||||
HSV,
|
||||
Invert,
|
||||
Kaleidoscope,
|
||||
Mask,
|
||||
Noise,
|
||||
Pixelate,
|
||||
Posterize,
|
||||
RGB,
|
||||
RGBA,
|
||||
Sepia,
|
||||
Solarize,
|
||||
Threshold,
|
||||
},
|
||||
});
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
import { Konva } from './_CoreInternals.ts';
|
||||
export default Konva;
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
var _a;
|
||||
var _b;
|
||||
import { Konva } from "./_CoreInternals.js";
|
||||
// @ts-ignore
|
||||
import * as Canvas from 'canvas';
|
||||
const canvas = Canvas['default'] || Canvas;
|
||||
// @ts-ignore
|
||||
global.DOMMatrix = canvas.DOMMatrix;
|
||||
// @ts-ignore
|
||||
(_a = (_b = global).Path2D) !== null && _a !== void 0 ? _a : (_b.Path2D = class Path2D {
|
||||
constructor(path) {
|
||||
this.path = path;
|
||||
}
|
||||
get [Symbol.toStringTag]() {
|
||||
return `Path2D`;
|
||||
}
|
||||
});
|
||||
Konva.Util['createCanvasElement'] = () => {
|
||||
const node = canvas.createCanvas(300, 300);
|
||||
if (!node['style']) {
|
||||
node['style'] = {};
|
||||
}
|
||||
return node;
|
||||
};
|
||||
// create image in Node env
|
||||
Konva.Util.createImageElement = () => {
|
||||
const node = new canvas.Image();
|
||||
return node;
|
||||
};
|
||||
Konva._renderBackend = 'node-canvas';
|
||||
// the core Konva object, without shapes and filters. `import 'konva'` extends
|
||||
// this same object with them
|
||||
export default Konva;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
/**
|
||||
* Blur Filter
|
||||
* @function
|
||||
* @name Blur
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Blur]);
|
||||
* node.blurRadius(10);
|
||||
*/
|
||||
export declare const Blur: Filter;
|
||||
/**
|
||||
* get/set blur radius. Use with {@link Konva.Filters.Blur} filter
|
||||
* @name Konva.Node#blurRadius
|
||||
* @method
|
||||
* @param {Integer} radius
|
||||
* @returns {Integer}
|
||||
*/
|
||||
+312
@@ -0,0 +1,312 @@
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Node } from "../Node.js";
|
||||
import { getNumberValidator } from "../Validators.js";
|
||||
/*
|
||||
the Gauss filter
|
||||
master repo: https://github.com/pavelpower/kineticjsGaussFilter
|
||||
*/
|
||||
/*
|
||||
|
||||
StackBlur - a fast almost Gaussian Blur For Canvas
|
||||
|
||||
Version: 0.5
|
||||
Author: Mario Klingemann
|
||||
Contact: mario@quasimondo.com
|
||||
Website: http://www.quasimondo.com/StackBlurForCanvas
|
||||
Twitter: @quasimondo
|
||||
|
||||
In case you find this class useful - especially in commercial projects -
|
||||
I am not totally unhappy for a small donation to my PayPal account
|
||||
mario@quasimondo.de
|
||||
|
||||
Or support me on flattr:
|
||||
https://flattr.com/thing/72791/StackBlur-a-fast-almost-Gaussian-Blur-Effect-for-CanvasJavascript
|
||||
|
||||
Copyright (c) 2010 Mario Klingemann
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
* @license MIT
|
||||
*/
|
||||
function BlurStack() {
|
||||
this.r = 0;
|
||||
this.g = 0;
|
||||
this.b = 0;
|
||||
this.a = 0;
|
||||
this.next = null;
|
||||
}
|
||||
const mul_table = [
|
||||
512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292,
|
||||
512, 454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292,
|
||||
273, 512, 482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259,
|
||||
496, 475, 456, 437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292,
|
||||
282, 273, 265, 512, 497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373,
|
||||
364, 354, 345, 337, 328, 320, 312, 305, 298, 291, 284, 278, 271, 265, 259,
|
||||
507, 496, 485, 475, 465, 456, 446, 437, 428, 420, 412, 404, 396, 388, 381,
|
||||
374, 367, 360, 354, 347, 341, 335, 329, 323, 318, 312, 307, 302, 297, 292,
|
||||
287, 282, 278, 273, 269, 265, 261, 512, 505, 497, 489, 482, 475, 468, 461,
|
||||
454, 447, 441, 435, 428, 422, 417, 411, 405, 399, 394, 389, 383, 378, 373,
|
||||
368, 364, 359, 354, 350, 345, 341, 337, 332, 328, 324, 320, 316, 312, 309,
|
||||
305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271, 268, 265, 262, 259,
|
||||
257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456, 451, 446, 442,
|
||||
437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388, 385, 381,
|
||||
377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335, 332,
|
||||
329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292,
|
||||
289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259,
|
||||
];
|
||||
const shg_table = [
|
||||
9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 17,
|
||||
17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19,
|
||||
19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
|
||||
20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
|
||||
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23,
|
||||
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||
23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 24, 24, 24,
|
||||
];
|
||||
// the horizontal pass blurs premultiplied colour, otherwise transparent
|
||||
// pixels bleed their colour into their neighbours; the vertical pass
|
||||
// unpremultiplies again
|
||||
function premultiply(channel, alpha) {
|
||||
return alpha === 255 ? channel : (channel * alpha) / 255;
|
||||
}
|
||||
// Preserve the maximum logical radius; high-DPI caches need larger kernels.
|
||||
const MAX_RADIUS = 180;
|
||||
function filterGaussBlurRGBA(imageData, radius) {
|
||||
const pixels = imageData.data, width = imageData.width, height = imageData.height;
|
||||
let p, yi, yw, r_sum, g_sum, b_sum, a_sum, r_out_sum, g_out_sum, b_out_sum, a_out_sum, r_in_sum, g_in_sum, b_in_sum, a_in_sum, pr, pg, pb, pa, rbs;
|
||||
const div = radius + radius + 1, widthMinus1 = width - 1, heightMinus1 = height - 1, radiusPlus1 = radius + 1, sumFactor = (radiusPlus1 * (radiusPlus1 + 1)) / 2, stackStart = new BlurStack(),
|
||||
// Use floating-point normalization to avoid 32-bit overflow at high DPI.
|
||||
normalization = radius <= MAX_RADIUS
|
||||
? mul_table[radius] / 2 ** shg_table[radius]
|
||||
: 1 / (radiusPlus1 * radiusPlus1);
|
||||
let stackEnd = null, stack = stackStart, stackIn = null, stackOut = null;
|
||||
for (let i = 1; i < div; i++) {
|
||||
stack = stack.next = new BlurStack();
|
||||
if (i === radiusPlus1) {
|
||||
stackEnd = stack;
|
||||
}
|
||||
}
|
||||
stack.next = stackStart;
|
||||
yw = yi = 0;
|
||||
for (let y = 0; y < height; y++) {
|
||||
r_in_sum =
|
||||
g_in_sum =
|
||||
b_in_sum =
|
||||
a_in_sum =
|
||||
r_sum =
|
||||
g_sum =
|
||||
b_sum =
|
||||
a_sum =
|
||||
0;
|
||||
pa = pixels[yi + 3];
|
||||
pr = premultiply(pixels[yi], pa);
|
||||
pg = premultiply(pixels[yi + 1], pa);
|
||||
pb = premultiply(pixels[yi + 2], pa);
|
||||
r_out_sum = radiusPlus1 * pr;
|
||||
g_out_sum = radiusPlus1 * pg;
|
||||
b_out_sum = radiusPlus1 * pb;
|
||||
a_out_sum = radiusPlus1 * pa;
|
||||
r_sum += sumFactor * pr;
|
||||
g_sum += sumFactor * pg;
|
||||
b_sum += sumFactor * pb;
|
||||
a_sum += sumFactor * pa;
|
||||
stack = stackStart;
|
||||
for (let i = 0; i < radiusPlus1; i++) {
|
||||
stack.r = pr;
|
||||
stack.g = pg;
|
||||
stack.b = pb;
|
||||
stack.a = pa;
|
||||
stack = stack.next;
|
||||
}
|
||||
for (let i = 1; i < radiusPlus1; i++) {
|
||||
p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2);
|
||||
pa = pixels[p + 3];
|
||||
r_sum +=
|
||||
(stack.r = pr = premultiply(pixels[p], pa)) * (rbs = radiusPlus1 - i);
|
||||
g_sum += (stack.g = pg = premultiply(pixels[p + 1], pa)) * rbs;
|
||||
b_sum += (stack.b = pb = premultiply(pixels[p + 2], pa)) * rbs;
|
||||
a_sum += (stack.a = pa) * rbs;
|
||||
r_in_sum += pr;
|
||||
g_in_sum += pg;
|
||||
b_in_sum += pb;
|
||||
a_in_sum += pa;
|
||||
stack = stack.next;
|
||||
}
|
||||
stackIn = stackStart;
|
||||
stackOut = stackEnd;
|
||||
for (let x = 0; x < width; x++) {
|
||||
pixels[yi] = Math.floor(r_sum * normalization);
|
||||
pixels[yi + 1] = Math.floor(g_sum * normalization);
|
||||
pixels[yi + 2] = Math.floor(b_sum * normalization);
|
||||
pixels[yi + 3] = Math.floor(a_sum * normalization);
|
||||
r_sum -= r_out_sum;
|
||||
g_sum -= g_out_sum;
|
||||
b_sum -= b_out_sum;
|
||||
a_sum -= a_out_sum;
|
||||
r_out_sum -= stackIn.r;
|
||||
g_out_sum -= stackIn.g;
|
||||
b_out_sum -= stackIn.b;
|
||||
a_out_sum -= stackIn.a;
|
||||
p = (yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1)) << 2;
|
||||
pa = pixels[p + 3];
|
||||
r_in_sum += stackIn.r = premultiply(pixels[p], pa);
|
||||
g_in_sum += stackIn.g = premultiply(pixels[p + 1], pa);
|
||||
b_in_sum += stackIn.b = premultiply(pixels[p + 2], pa);
|
||||
a_in_sum += stackIn.a = pa;
|
||||
r_sum += r_in_sum;
|
||||
g_sum += g_in_sum;
|
||||
b_sum += b_in_sum;
|
||||
a_sum += a_in_sum;
|
||||
stackIn = stackIn.next;
|
||||
r_out_sum += pr = stackOut.r;
|
||||
g_out_sum += pg = stackOut.g;
|
||||
b_out_sum += pb = stackOut.b;
|
||||
a_out_sum += pa = stackOut.a;
|
||||
r_in_sum -= pr;
|
||||
g_in_sum -= pg;
|
||||
b_in_sum -= pb;
|
||||
a_in_sum -= pa;
|
||||
stackOut = stackOut.next;
|
||||
yi += 4;
|
||||
}
|
||||
yw += width;
|
||||
}
|
||||
for (let x = 0; x < width; x++) {
|
||||
g_in_sum =
|
||||
b_in_sum =
|
||||
a_in_sum =
|
||||
r_in_sum =
|
||||
g_sum =
|
||||
b_sum =
|
||||
a_sum =
|
||||
r_sum =
|
||||
0;
|
||||
yi = x << 2;
|
||||
r_out_sum = radiusPlus1 * (pr = pixels[yi]);
|
||||
g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]);
|
||||
b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]);
|
||||
a_out_sum = radiusPlus1 * (pa = pixels[yi + 3]);
|
||||
r_sum += sumFactor * pr;
|
||||
g_sum += sumFactor * pg;
|
||||
b_sum += sumFactor * pb;
|
||||
a_sum += sumFactor * pa;
|
||||
stack = stackStart;
|
||||
for (let i = 0; i < radiusPlus1; i++) {
|
||||
stack.r = pr;
|
||||
stack.g = pg;
|
||||
stack.b = pb;
|
||||
stack.a = pa;
|
||||
stack = stack.next;
|
||||
}
|
||||
// the pass primes from the second row, which a 1-pixel-tall image lacks
|
||||
let yp = Math.min(1, heightMinus1) * width;
|
||||
for (let i = 1; i <= radius; i++) {
|
||||
yi = (yp + x) << 2;
|
||||
r_sum += (stack.r = pr = pixels[yi]) * (rbs = radiusPlus1 - i);
|
||||
g_sum += (stack.g = pg = pixels[yi + 1]) * rbs;
|
||||
b_sum += (stack.b = pb = pixels[yi + 2]) * rbs;
|
||||
a_sum += (stack.a = pa = pixels[yi + 3]) * rbs;
|
||||
r_in_sum += pr;
|
||||
g_in_sum += pg;
|
||||
b_in_sum += pb;
|
||||
a_in_sum += pa;
|
||||
stack = stack.next;
|
||||
if (i < heightMinus1) {
|
||||
yp += width;
|
||||
}
|
||||
}
|
||||
yi = x;
|
||||
stackIn = stackStart;
|
||||
stackOut = stackEnd;
|
||||
for (let y = 0; y < height; y++) {
|
||||
p = yi << 2;
|
||||
pixels[p + 3] = pa = Math.floor(a_sum * normalization);
|
||||
if (pa > 0) {
|
||||
pa = 255 / pa;
|
||||
pixels[p] = Math.floor(r_sum * normalization) * pa;
|
||||
pixels[p + 1] = Math.floor(g_sum * normalization) * pa;
|
||||
pixels[p + 2] = Math.floor(b_sum * normalization) * pa;
|
||||
}
|
||||
else {
|
||||
pixels[p] = pixels[p + 1] = pixels[p + 2] = 0;
|
||||
}
|
||||
r_sum -= r_out_sum;
|
||||
g_sum -= g_out_sum;
|
||||
b_sum -= b_out_sum;
|
||||
a_sum -= a_out_sum;
|
||||
r_out_sum -= stackIn.r;
|
||||
g_out_sum -= stackIn.g;
|
||||
b_out_sum -= stackIn.b;
|
||||
a_out_sum -= stackIn.a;
|
||||
p =
|
||||
(x +
|
||||
((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width) <<
|
||||
2;
|
||||
r_sum += r_in_sum += stackIn.r = pixels[p];
|
||||
g_sum += g_in_sum += stackIn.g = pixels[p + 1];
|
||||
b_sum += b_in_sum += stackIn.b = pixels[p + 2];
|
||||
a_sum += a_in_sum += stackIn.a = pixels[p + 3];
|
||||
stackIn = stackIn.next;
|
||||
r_out_sum += pr = stackOut.r;
|
||||
g_out_sum += pg = stackOut.g;
|
||||
b_out_sum += pb = stackOut.b;
|
||||
a_out_sum += pa = stackOut.a;
|
||||
r_in_sum -= pr;
|
||||
g_in_sum -= pg;
|
||||
b_in_sum -= pb;
|
||||
a_in_sum -= pa;
|
||||
stackOut = stackOut.next;
|
||||
yi += width;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Blur Filter
|
||||
* @function
|
||||
* @name Blur
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Blur]);
|
||||
* node.blurRadius(10);
|
||||
*/
|
||||
export const Blur = function Blur(imageData, pixelRatio = 1) {
|
||||
const radius = Math.round(Math.min(this.blurRadius(), MAX_RADIUS) * pixelRatio);
|
||||
if (radius > 0) {
|
||||
filterGaussBlurRGBA(imageData, radius);
|
||||
}
|
||||
};
|
||||
Factory.addGetterSetter(Node, 'blurRadius', 0, getNumberValidator(), Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set blur radius. Use with {@link Konva.Filters.Blur} filter
|
||||
* @name Konva.Node#blurRadius
|
||||
* @method
|
||||
* @param {Integer} radius
|
||||
* @returns {Integer}
|
||||
*/
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
/**
|
||||
* Brighten Filter.
|
||||
* @deprecated Use {@link Konva.Filters.Brightness} instead for CSS-compatible behavior.
|
||||
* This filter uses additive brightness adjustment (adds a constant value to RGB channels).
|
||||
* @function
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Brighten]);
|
||||
* node.brightness(0.8);
|
||||
*/
|
||||
export declare const Brighten: Filter;
|
||||
/**
|
||||
* get/set filter brightness. With {@link Konva.Filters.Brighten} it is a number between -1 and 1
|
||||
* added to every channel: positive values brighten the pixels and negative values darken them.
|
||||
* With {@link Konva.Filters.Brightness} it is a CSS-like multiplier: 1 is no change, 1.5 is 50% brighter.
|
||||
* Both filters leave pixels unchanged when the attribute is unset. An explicit 0 makes Brightness black.
|
||||
* @name Konva.Node#brightness
|
||||
* @method
|
||||
* @param {Number} brightness
|
||||
* @returns {Number}
|
||||
*/
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Node } from "../Node.js";
|
||||
import { getNumberValidator } from "../Validators.js";
|
||||
/**
|
||||
* Brighten Filter.
|
||||
* @deprecated Use {@link Konva.Filters.Brightness} instead for CSS-compatible behavior.
|
||||
* This filter uses additive brightness adjustment (adds a constant value to RGB channels).
|
||||
* @function
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Brighten]);
|
||||
* node.brightness(0.8);
|
||||
*/
|
||||
export const Brighten = function (imageData) {
|
||||
const brightness = this.brightness() * 255, data = imageData.data, len = data.length;
|
||||
for (let i = 0; i < len; i += 4) {
|
||||
// red
|
||||
data[i] += brightness;
|
||||
// green
|
||||
data[i + 1] += brightness;
|
||||
// blue
|
||||
data[i + 2] += brightness;
|
||||
}
|
||||
};
|
||||
Factory.addGetterSetter(Node, 'brightness', 0, getNumberValidator(), Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set filter brightness. With {@link Konva.Filters.Brighten} it is a number between -1 and 1
|
||||
* added to every channel: positive values brighten the pixels and negative values darken them.
|
||||
* With {@link Konva.Filters.Brightness} it is a CSS-like multiplier: 1 is no change, 1.5 is 50% brighter.
|
||||
* Both filters leave pixels unchanged when the attribute is unset. An explicit 0 makes Brightness black.
|
||||
* @name Konva.Node#brightness
|
||||
* @method
|
||||
* @param {Number} brightness
|
||||
* @returns {Number}
|
||||
*/
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
import './Brighten.ts';
|
||||
/**
|
||||
* Brightness Filter.
|
||||
* CSS-compatible brightness multiplier. An unset brightness leaves pixels unchanged.
|
||||
* @function
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Brightness]);
|
||||
* node.brightness(1.5); // 50% brighter (CSS-compatible)
|
||||
*/
|
||||
export declare const Brightness: Filter;
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// The shared brightness accessor is registered by Brighten.
|
||||
import "./Brighten.js";
|
||||
/**
|
||||
* Brightness Filter.
|
||||
* CSS-compatible brightness multiplier. An unset brightness leaves pixels unchanged.
|
||||
* @function
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Brightness]);
|
||||
* node.brightness(1.5); // 50% brighter (CSS-compatible)
|
||||
*/
|
||||
export const Brightness = function (imageData) {
|
||||
const brightness = this.attrs.brightness === undefined ? 1 : this.brightness(), data = imageData.data, len = data.length;
|
||||
for (let i = 0; i < len; i += 4) {
|
||||
// red
|
||||
data[i] = Math.min(255, data[i] * brightness);
|
||||
// green
|
||||
data[i + 1] = Math.min(255, data[i + 1] * brightness);
|
||||
// blue
|
||||
data[i + 2] = Math.min(255, data[i + 2] * brightness);
|
||||
}
|
||||
};
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
/**
|
||||
* Contrast Filter.
|
||||
* @function
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Contrast]);
|
||||
* node.contrast(10);
|
||||
*/
|
||||
export declare const Contrast: Filter;
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Node } from "../Node.js";
|
||||
import { getNumberValidator } from "../Validators.js";
|
||||
/**
|
||||
* Contrast Filter.
|
||||
* @function
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Contrast]);
|
||||
* node.contrast(10);
|
||||
*/
|
||||
export const Contrast = function (imageData) {
|
||||
const adjust = Math.pow((this.contrast() + 100) / 100, 2);
|
||||
const data = imageData.data, nPixels = data.length;
|
||||
let red = 150, green = 150, blue = 150;
|
||||
for (let i = 0; i < nPixels; i += 4) {
|
||||
red = data[i];
|
||||
green = data[i + 1];
|
||||
blue = data[i + 2];
|
||||
//Red channel
|
||||
red /= 255;
|
||||
red -= 0.5;
|
||||
red *= adjust;
|
||||
red += 0.5;
|
||||
red *= 255;
|
||||
//Green channel
|
||||
green /= 255;
|
||||
green -= 0.5;
|
||||
green *= adjust;
|
||||
green += 0.5;
|
||||
green *= 255;
|
||||
//Blue channel
|
||||
blue /= 255;
|
||||
blue -= 0.5;
|
||||
blue *= adjust;
|
||||
blue += 0.5;
|
||||
blue *= 255;
|
||||
red = red < 0 ? 0 : red > 255 ? 255 : red;
|
||||
green = green < 0 ? 0 : green > 255 ? 255 : green;
|
||||
blue = blue < 0 ? 0 : blue > 255 ? 255 : blue;
|
||||
data[i] = red;
|
||||
data[i + 1] = green;
|
||||
data[i + 2] = blue;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* get/set filter contrast. The contrast is a number between -100 and 100.
|
||||
* Use with {@link Konva.Filters.Contrast} filter.
|
||||
* @name Konva.Node#contrast
|
||||
* @method
|
||||
* @param {Number} contrast value between -100 and 100
|
||||
* @returns {Number}
|
||||
*/
|
||||
Factory.addGetterSetter(Node, 'contrast', 0, getNumberValidator(), Factory.afterSetFilter);
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
/**
|
||||
* Emboss Filter.
|
||||
* @function
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Emboss]);
|
||||
* node.embossStrength(0.8);
|
||||
* node.embossWhiteLevel(0.3);
|
||||
* node.embossDirection('right');
|
||||
* node.embossBlend(true);
|
||||
*/
|
||||
export declare const Emboss: Filter;
|
||||
/**
|
||||
* get/set emboss blend. Use with {@link Konva.Filters.Emboss} filter.
|
||||
* @name Konva.Node#embossBlend
|
||||
* @method
|
||||
* @param {Boolean} embossBlend
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Node } from "../Node.js";
|
||||
import { getNumberValidator } from "../Validators.js";
|
||||
/**
|
||||
* Emboss Filter.
|
||||
* @function
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Emboss]);
|
||||
* node.embossStrength(0.8);
|
||||
* node.embossWhiteLevel(0.3);
|
||||
* node.embossDirection('right');
|
||||
* node.embossBlend(true);
|
||||
*/
|
||||
export const Emboss = function (imageData) {
|
||||
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
||||
const data = imageData.data;
|
||||
const w = imageData.width;
|
||||
const h = imageData.height;
|
||||
// Inputs from Konva node
|
||||
const strength01 = Math.min(1, Math.max(0, (_b = (_a = this.embossStrength) === null || _a === void 0 ? void 0 : _a.call(this)) !== null && _b !== void 0 ? _b : 0.5)); // [0..1]
|
||||
const whiteLevel01 = Math.min(1, Math.max(0, (_d = (_c = this.embossWhiteLevel) === null || _c === void 0 ? void 0 : _c.call(this)) !== null && _d !== void 0 ? _d : 0.5)); // [0..1]
|
||||
// Convert string direction to degrees
|
||||
const directionMap = {
|
||||
'top-left': 315,
|
||||
top: 270,
|
||||
'top-right': 225,
|
||||
right: 180,
|
||||
'bottom-right': 135,
|
||||
bottom: 90,
|
||||
'bottom-left': 45,
|
||||
left: 0,
|
||||
};
|
||||
const directionDeg = (_g = directionMap[(_f = (_e = this.embossDirection) === null || _e === void 0 ? void 0 : _e.call(this)) !== null && _f !== void 0 ? _f : 'top-left']) !== null && _g !== void 0 ? _g : 315; // degrees
|
||||
const blend = !!((_j = (_h = this.embossBlend) === null || _h === void 0 ? void 0 : _h.call(this)) !== null && _j !== void 0 ? _j : false);
|
||||
// Internal mapping:
|
||||
// - "strength" was 0..10; we honor your 0..1 API and scale accordingly.
|
||||
// - Sobel directional response is roughly in [-1020..1020] for 8-bit luminance; scale to ~±128.
|
||||
const strength = strength01 * 10;
|
||||
const bias = whiteLevel01 * 255;
|
||||
const dirRad = (directionDeg * Math.PI) / 180;
|
||||
const cx = Math.cos(dirRad);
|
||||
const cy = Math.sin(dirRad);
|
||||
const SCALE = (128 / 1020) * strength; // ≈0.1255 * strength
|
||||
// Precompute luminance (Rec.709)
|
||||
const lum = new Float32Array(w * h);
|
||||
for (let p = 0, i = 0; i < data.length; i += 4, p++) {
|
||||
lum[p] = 0.2126 * data[i] + 0.7152 * data[i + 1] + 0.0722 * data[i + 2];
|
||||
}
|
||||
// Sobel kernels (flattened)
|
||||
const Gx = [-1, 0, 1, -2, 0, 2, -1, 0, 1];
|
||||
const Gy = [-1, -2, -1, 0, 0, 0, 1, 2, 1];
|
||||
const clamp8 = (v) => (v < 0 ? 0 : v > 255 ? 255 : v);
|
||||
// Extend the edge pixels so the same kernel applies at every position.
|
||||
for (let y = 0; y < h; y++) {
|
||||
for (let x = 0; x < w; x++) {
|
||||
let sx = 0, sy = 0, k = 0;
|
||||
for (let dy = -1; dy <= 1; dy++) {
|
||||
const row = Math.max(0, Math.min(h - 1, y + dy)) * w;
|
||||
for (let dx = -1; dx <= 1; dx++, k++) {
|
||||
const sample = lum[row + Math.max(0, Math.min(w - 1, x + dx))];
|
||||
sx += sample * Gx[k];
|
||||
sy += sample * Gy[k];
|
||||
}
|
||||
}
|
||||
const p = y * w + x;
|
||||
const r = cx * sx + cy * sy; // directional response
|
||||
const outGray = clamp8(bias + r * SCALE); // biased, scaled, clamped
|
||||
const o = p * 4;
|
||||
if (blend) {
|
||||
// Add the emboss "relief" around chosen bias to original RGB
|
||||
const delta = outGray - bias; // symmetric around whiteLevel
|
||||
data[o] = clamp8(data[o] + delta);
|
||||
data[o + 1] = clamp8(data[o + 1] + delta);
|
||||
data[o + 2] = clamp8(data[o + 2] + delta);
|
||||
}
|
||||
else {
|
||||
// Grayscale embossed output
|
||||
data[o] = data[o + 1] = data[o + 2] = outGray;
|
||||
}
|
||||
}
|
||||
}
|
||||
return imageData;
|
||||
};
|
||||
Factory.addGetterSetter(Node, 'embossStrength', 0.5, getNumberValidator(), Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set emboss strength. Use with {@link Konva.Filters.Emboss} filter.
|
||||
* @name Konva.Node#embossStrength
|
||||
* @method
|
||||
* @param {Number} level between 0 and 1. Default is 0.5
|
||||
* @returns {Number}
|
||||
*/
|
||||
Factory.addGetterSetter(Node, 'embossWhiteLevel', 0.5, getNumberValidator(), Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set emboss white level. Use with {@link Konva.Filters.Emboss} filter.
|
||||
* @name Konva.Node#embossWhiteLevel
|
||||
* @method
|
||||
* @param {Number} embossWhiteLevel between 0 and 1. Default is 0.5
|
||||
* @returns {Number}
|
||||
*/
|
||||
Factory.addGetterSetter(Node, 'embossDirection', 'top-left', undefined, Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set emboss direction. Use with {@link Konva.Filters.Emboss} filter.
|
||||
* @name Konva.Node#embossDirection
|
||||
* @method
|
||||
* @param {String} embossDirection can be top-left, top, top-right, right, bottom-right, bottom, bottom-left or left
|
||||
* The default is top-left
|
||||
* @returns {String}
|
||||
*/
|
||||
Factory.addGetterSetter(Node, 'embossBlend', false, undefined, Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set emboss blend. Use with {@link Konva.Filters.Emboss} filter.
|
||||
* @name Konva.Node#embossBlend
|
||||
* @method
|
||||
* @param {Boolean} embossBlend
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
/**
|
||||
* Enhance Filter. Adjusts the colors so that they span the widest
|
||||
* possible range (ie 0-255). Performs w*h pixel reads and w*h pixel
|
||||
* writes.
|
||||
* @function
|
||||
* @name Enhance
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @author ippo615
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Enhance]);
|
||||
* node.enhance(0.4);
|
||||
*/
|
||||
export declare const Enhance: Filter;
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Node } from "../Node.js";
|
||||
import { getNumberValidator } from "../Validators.js";
|
||||
function remap(fromValue, fromMin, fromMax, toMin, toMax) {
|
||||
// Compute the range of the data
|
||||
const fromRange = fromMax - fromMin, toRange = toMax - toMin;
|
||||
// If either range is 0, then the value can only be mapped to 1 value
|
||||
if (fromRange === 0) {
|
||||
return toMin + toRange / 2;
|
||||
}
|
||||
if (toRange === 0) {
|
||||
return toMin;
|
||||
}
|
||||
// (1) untranslate, (2) unscale, (3) rescale, (4) retranslate
|
||||
let toValue = (fromValue - fromMin) / fromRange;
|
||||
toValue = toRange * toValue + toMin;
|
||||
return toValue;
|
||||
}
|
||||
/**
|
||||
* Enhance Filter. Adjusts the colors so that they span the widest
|
||||
* possible range (ie 0-255). Performs w*h pixel reads and w*h pixel
|
||||
* writes.
|
||||
* @function
|
||||
* @name Enhance
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @author ippo615
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Enhance]);
|
||||
* node.enhance(0.4);
|
||||
*/
|
||||
export const Enhance = function (imageData) {
|
||||
const data = imageData.data, nSubPixels = data.length;
|
||||
let rMin = data[0], rMax = rMin, r, gMin = data[1], gMax = gMin, g, bMin = data[2], bMax = bMin, b;
|
||||
// If we are not enhancing anything - don't do any computation
|
||||
const enhanceAmount = this.enhance();
|
||||
if (enhanceAmount === 0) {
|
||||
return;
|
||||
}
|
||||
// 1st Pass - find the min and max for each channel:
|
||||
for (let i = 0; i < nSubPixels; i += 4) {
|
||||
r = data[i + 0];
|
||||
if (r < rMin) {
|
||||
rMin = r;
|
||||
}
|
||||
else if (r > rMax) {
|
||||
rMax = r;
|
||||
}
|
||||
g = data[i + 1];
|
||||
if (g < gMin) {
|
||||
gMin = g;
|
||||
}
|
||||
else if (g > gMax) {
|
||||
gMax = g;
|
||||
}
|
||||
b = data[i + 2];
|
||||
if (b < bMin) {
|
||||
bMin = b;
|
||||
}
|
||||
else if (b > bMax) {
|
||||
bMax = b;
|
||||
}
|
||||
//a = data[i + 3];
|
||||
//if (a < aMin) { aMin = a; } else
|
||||
//if (a > aMax) { aMax = a; }
|
||||
}
|
||||
// If there is only 1 level - don't remap
|
||||
if (rMax === rMin) {
|
||||
rMax = 255;
|
||||
rMin = 0;
|
||||
}
|
||||
if (gMax === gMin) {
|
||||
gMax = 255;
|
||||
gMin = 0;
|
||||
}
|
||||
if (bMax === bMin) {
|
||||
bMax = 255;
|
||||
bMin = 0;
|
||||
}
|
||||
let rGoalMax, rGoalMin, gGoalMax, gGoalMin, bGoalMax, bGoalMin;
|
||||
// If the enhancement is positive - stretch the histogram
|
||||
if (enhanceAmount > 0) {
|
||||
rGoalMax = rMax + enhanceAmount * (255 - rMax);
|
||||
rGoalMin = rMin - enhanceAmount * (rMin - 0);
|
||||
gGoalMax = gMax + enhanceAmount * (255 - gMax);
|
||||
gGoalMin = gMin - enhanceAmount * (gMin - 0);
|
||||
bGoalMax = bMax + enhanceAmount * (255 - bMax);
|
||||
bGoalMin = bMin - enhanceAmount * (bMin - 0);
|
||||
// If the enhancement is negative - compress the histogram
|
||||
}
|
||||
else {
|
||||
const rMid = (rMax + rMin) * 0.5;
|
||||
rGoalMax = rMax + enhanceAmount * (rMax - rMid);
|
||||
rGoalMin = rMin + enhanceAmount * (rMin - rMid);
|
||||
const gMid = (gMax + gMin) * 0.5;
|
||||
gGoalMax = gMax + enhanceAmount * (gMax - gMid);
|
||||
gGoalMin = gMin + enhanceAmount * (gMin - gMid);
|
||||
const bMid = (bMax + bMin) * 0.5;
|
||||
bGoalMax = bMax + enhanceAmount * (bMax - bMid);
|
||||
bGoalMin = bMin + enhanceAmount * (bMin - bMid);
|
||||
}
|
||||
// Pass 2 - remap everything, except the alpha
|
||||
for (let i = 0; i < nSubPixels; i += 4) {
|
||||
data[i + 0] = remap(data[i + 0], rMin, rMax, rGoalMin, rGoalMax);
|
||||
data[i + 1] = remap(data[i + 1], gMin, gMax, gGoalMin, gGoalMax);
|
||||
data[i + 2] = remap(data[i + 2], bMin, bMax, bGoalMin, bGoalMax);
|
||||
//data[i + 3] = remap(data[i + 3], aMin, aMax, aGoalMin, aGoalMax);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* get/set enhance. Use with {@link Konva.Filters.Enhance} filter. -1 to 1 values
|
||||
* @name Konva.Node#enhance
|
||||
* @method
|
||||
* @param {Float} amount
|
||||
* @returns {Float}
|
||||
*/
|
||||
Factory.addGetterSetter(Node, 'enhance', 0, getNumberValidator(), Factory.afterSetFilter);
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
/**
|
||||
* Grayscale Filter
|
||||
* @function
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Grayscale]);
|
||||
*/
|
||||
export declare const Grayscale: Filter;
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Grayscale Filter
|
||||
* @function
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Grayscale]);
|
||||
*/
|
||||
export const Grayscale = function (imageData) {
|
||||
const data = imageData.data, len = data.length;
|
||||
for (let i = 0; i < len; i += 4) {
|
||||
const brightness = 0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2];
|
||||
// red
|
||||
data[i] = brightness;
|
||||
// green
|
||||
data[i + 1] = brightness;
|
||||
// blue
|
||||
data[i + 2] = brightness;
|
||||
}
|
||||
};
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
/**
|
||||
* get/set hsl luminance. Use with {@link Konva.Filters.HSL} filter.
|
||||
* @name Konva.Node#luminance
|
||||
* @method
|
||||
* @param {Number} value from -1 to 1
|
||||
* @returns {Number}
|
||||
*/
|
||||
/**
|
||||
* HSL Filter. Adjusts the hue, saturation and luminance (or lightness)
|
||||
* @function
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @author ippo615
|
||||
* @example
|
||||
* image.filters([Konva.Filters.HSL]);
|
||||
* image.luminance(0.2);
|
||||
*/
|
||||
export declare const HSL: Filter;
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Node } from "../Node.js";
|
||||
import { getNumberValidator } from "../Validators.js";
|
||||
Factory.addGetterSetter(Node, 'hue', 0, getNumberValidator(), Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set hsv hue in degrees. Use with {@link Konva.Filters.HSV} or {@link Konva.Filters.HSL} filter.
|
||||
* @name Konva.Node#hue
|
||||
* @method
|
||||
* @param {Number} hue value between 0 and 359
|
||||
* @returns {Number}
|
||||
*/
|
||||
Factory.addGetterSetter(Node, 'saturation', 0, getNumberValidator(), Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set hsv saturation. Use with {@link Konva.Filters.HSV} or {@link Konva.Filters.HSL} filter.
|
||||
* @name Konva.Node#saturation
|
||||
* @method
|
||||
* @param {Number} saturation 0 is no change, -1.0 halves the saturation, 1.0 doubles, etc..
|
||||
* @returns {Number}
|
||||
*/
|
||||
Factory.addGetterSetter(Node, 'luminance', 0, getNumberValidator(), Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set hsl luminance. Use with {@link Konva.Filters.HSL} filter.
|
||||
* @name Konva.Node#luminance
|
||||
* @method
|
||||
* @param {Number} value from -1 to 1
|
||||
* @returns {Number}
|
||||
*/
|
||||
/**
|
||||
* HSL Filter. Adjusts the hue, saturation and luminance (or lightness)
|
||||
* @function
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @author ippo615
|
||||
* @example
|
||||
* image.filters([Konva.Filters.HSL]);
|
||||
* image.luminance(0.2);
|
||||
*/
|
||||
export const HSL = function (imageData) {
|
||||
const data = imageData.data, nPixels = data.length, v = 1, s = Math.pow(2, this.saturation()), h = ((this.hue() % 360) + 360) % 360, l = this.luminance() * 127;
|
||||
// Basis for the technique used:
|
||||
// http://beesbuzz.biz/code/hsv_color_transforms.php
|
||||
// V is the value multiplier (1 for none, 2 for double, 0.5 for half)
|
||||
// S is the saturation multiplier (1 for none, 2 for double, 0.5 for half)
|
||||
// H is the hue shift in degrees (0 to 360)
|
||||
// vsu = V*S*cos(H*PI/180);
|
||||
// vsw = V*S*sin(H*PI/180);
|
||||
//[ .299V+.701vsu+.168vsw .587V-.587vsu+.330vsw .114V-.114vsu-.497vsw ] [R]
|
||||
//[ .299V-.299vsu-.328vsw .587V+.413vsu+.035vsw .114V-.114vsu+.292vsw ]*[G]
|
||||
//[ .299V-.300vsu+1.25vsw .587V-.588vsu-1.05vsw .114V+.886vsu-.203vsw ] [B]
|
||||
// Precompute the values in the matrix:
|
||||
const vsu = v * s * Math.cos((h * Math.PI) / 180), vsw = v * s * Math.sin((h * Math.PI) / 180);
|
||||
// (result spot)(source spot)
|
||||
const rr = 0.299 * v + 0.701 * vsu + 0.168 * vsw, rg = 0.587 * v - 0.587 * vsu + 0.33 * vsw, rb = 0.114 * v - 0.114 * vsu - 0.497 * vsw;
|
||||
const gr = 0.299 * v - 0.299 * vsu - 0.328 * vsw, gg = 0.587 * v + 0.413 * vsu + 0.035 * vsw, gb = 0.114 * v - 0.114 * vsu + 0.292 * vsw;
|
||||
const br = 0.299 * v - 0.3 * vsu + 1.25 * vsw, bg = 0.587 * v - 0.588 * vsu - 1.05 * vsw, bb = 0.114 * v + 0.886 * vsu - 0.203 * vsw;
|
||||
let r, g, b, a;
|
||||
for (let i = 0; i < nPixels; i += 4) {
|
||||
r = data[i + 0];
|
||||
g = data[i + 1];
|
||||
b = data[i + 2];
|
||||
a = data[i + 3];
|
||||
data[i + 0] = rr * r + rg * g + rb * b + l;
|
||||
data[i + 1] = gr * r + gg * g + gb * b + l;
|
||||
data[i + 2] = br * r + bg * g + bb * b + l;
|
||||
data[i + 3] = a; // alpha
|
||||
}
|
||||
};
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
/**
|
||||
* HSV Filter. Adjusts the hue, saturation and value
|
||||
* @function
|
||||
* @name HSV
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @author ippo615
|
||||
* @example
|
||||
* image.filters([Konva.Filters.HSV]);
|
||||
* image.value(1); // twice as bright
|
||||
*/
|
||||
export declare const HSV: Filter;
|
||||
/**
|
||||
* get/set hsv value. Use with {@link Konva.Filters.HSV} filter.
|
||||
* @name Konva.Node#value
|
||||
* @method
|
||||
* @param {Number} value 0 is no change, -1.0 halves the value, 1.0 doubles, etc..
|
||||
* @returns {Number}
|
||||
*/
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Node } from "../Node.js";
|
||||
import { getNumberValidator } from "../Validators.js";
|
||||
/**
|
||||
* HSV Filter. Adjusts the hue, saturation and value
|
||||
* @function
|
||||
* @name HSV
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @author ippo615
|
||||
* @example
|
||||
* image.filters([Konva.Filters.HSV]);
|
||||
* image.value(1); // twice as bright
|
||||
*/
|
||||
export const HSV = function (imageData) {
|
||||
const data = imageData.data, nPixels = data.length, v = Math.pow(2, this.value()), s = Math.pow(2, this.saturation()), h = ((this.hue() % 360) + 360) % 360;
|
||||
// Basis for the technique used:
|
||||
// http://beesbuzz.biz/code/hsv_color_transforms.php
|
||||
// V is the value multiplier (1 for none, 2 for double, 0.5 for half)
|
||||
// S is the saturation multiplier (1 for none, 2 for double, 0.5 for half)
|
||||
// H is the hue shift in degrees (0 to 360)
|
||||
// vsu = V*S*cos(H*PI/180);
|
||||
// vsw = V*S*sin(H*PI/180);
|
||||
//[ .299V+.701vsu+.168vsw .587V-.587vsu+.330vsw .114V-.114vsu-.497vsw ] [R]
|
||||
//[ .299V-.299vsu-.328vsw .587V+.413vsu+.035vsw .114V-.114vsu+.292vsw ]*[G]
|
||||
//[ .299V-.300vsu+1.25vsw .587V-.588vsu-1.05vsw .114V+.886vsu-.203vsw ] [B]
|
||||
// Precompute the values in the matrix:
|
||||
const vsu = v * s * Math.cos((h * Math.PI) / 180), vsw = v * s * Math.sin((h * Math.PI) / 180);
|
||||
// (result spot)(source spot)
|
||||
const rr = 0.299 * v + 0.701 * vsu + 0.168 * vsw, rg = 0.587 * v - 0.587 * vsu + 0.33 * vsw, rb = 0.114 * v - 0.114 * vsu - 0.497 * vsw;
|
||||
const gr = 0.299 * v - 0.299 * vsu - 0.328 * vsw, gg = 0.587 * v + 0.413 * vsu + 0.035 * vsw, gb = 0.114 * v - 0.114 * vsu + 0.292 * vsw;
|
||||
const br = 0.299 * v - 0.3 * vsu + 1.25 * vsw, bg = 0.587 * v - 0.588 * vsu - 1.05 * vsw, bb = 0.114 * v + 0.886 * vsu - 0.203 * vsw;
|
||||
for (let i = 0; i < nPixels; i += 4) {
|
||||
const r = data[i + 0];
|
||||
const g = data[i + 1];
|
||||
const b = data[i + 2];
|
||||
const a = data[i + 3];
|
||||
data[i + 0] = rr * r + rg * g + rb * b;
|
||||
data[i + 1] = gr * r + gg * g + gb * b;
|
||||
data[i + 2] = br * r + bg * g + bb * b;
|
||||
data[i + 3] = a; // alpha
|
||||
}
|
||||
};
|
||||
Factory.addGetterSetter(Node, 'hue', 0, getNumberValidator(), Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set hsv hue in degrees. Use with {@link Konva.Filters.HSV} or {@link Konva.Filters.HSL} filter.
|
||||
* @name Konva.Node#hue
|
||||
* @method
|
||||
* @param {Number} hue value between 0 and 359
|
||||
* @returns {Number}
|
||||
*/
|
||||
Factory.addGetterSetter(Node, 'saturation', 0, getNumberValidator(), Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set hsv saturation. Use with {@link Konva.Filters.HSV} or {@link Konva.Filters.HSL} filter.
|
||||
* @name Konva.Node#saturation
|
||||
* @method
|
||||
* @param {Number} saturation 0 is no change, -1.0 halves the saturation, 1.0 doubles, etc..
|
||||
* @returns {Number}
|
||||
*/
|
||||
Factory.addGetterSetter(Node, 'value', 0, getNumberValidator(), Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set hsv value. Use with {@link Konva.Filters.HSV} filter.
|
||||
* @name Konva.Node#value
|
||||
* @method
|
||||
* @param {Number} value 0 is no change, -1.0 halves the value, 1.0 doubles, etc..
|
||||
* @returns {Number}
|
||||
*/
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
/**
|
||||
* Invert Filter
|
||||
* @function
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Invert]);
|
||||
*/
|
||||
export declare const Invert: Filter;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Invert Filter
|
||||
* @function
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Invert]);
|
||||
*/
|
||||
export const Invert = function (imageData) {
|
||||
const data = imageData.data, len = data.length;
|
||||
for (let i = 0; i < len; i += 4) {
|
||||
// red
|
||||
data[i] = 255 - data[i];
|
||||
// green
|
||||
data[i + 1] = 255 - data[i + 1];
|
||||
// blue
|
||||
data[i + 2] = 255 - data[i + 2];
|
||||
}
|
||||
};
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
/**
|
||||
* Kaleidoscope Filter.
|
||||
* @function
|
||||
* @name Kaleidoscope
|
||||
* @author ippo615
|
||||
* @memberof Konva.Filters
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Kaleidoscope]);
|
||||
* node.kaleidoscopePower(3);
|
||||
* node.kaleidoscopeAngle(45);
|
||||
*/
|
||||
export declare const Kaleidoscope: Filter;
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Node } from "../Node.js";
|
||||
import { getNumberValidator } from "../Validators.js";
|
||||
/*
|
||||
* ToPolar Filter. Converts image data to polar coordinates. Performs
|
||||
* w*h*4 pixel reads and w*h pixel writes. The r axis is placed along
|
||||
* what would be the y axis and the theta axis along the x axis.
|
||||
* @function
|
||||
* @author ippo615
|
||||
* @memberof Konva.Filters
|
||||
* @param {ImageData} src, the source image data (what will be transformed)
|
||||
* @param {ImageData} dst, the destination image data (where it will be saved)
|
||||
* @param {Object} opt
|
||||
* @param {Number} [opt.polarCenterX] horizontal location for the center of the circle,
|
||||
* default is in the middle
|
||||
* @param {Number} [opt.polarCenterY] vertical location for the center of the circle,
|
||||
* default is in the middle
|
||||
*/
|
||||
const ToPolar = function (src, dst, opt) {
|
||||
const srcPixels = src.data, dstPixels = dst.data, xSize = src.width, ySize = src.height, xMid = opt.polarCenterX || xSize / 2, yMid = opt.polarCenterY || ySize / 2;
|
||||
// Find the largest radius
|
||||
let rMax = Math.sqrt(xMid * xMid + yMid * yMid);
|
||||
let x = xSize - xMid;
|
||||
let y = ySize - yMid;
|
||||
const rad = Math.sqrt(x * x + y * y);
|
||||
rMax = rad > rMax ? rad : rMax;
|
||||
// We'll be using y as the radius, and x as the angle (theta=t)
|
||||
const rSize = ySize, tSize = xSize;
|
||||
// We want to cover all angles (0-360) and we need to convert to
|
||||
// radians (*PI/180)
|
||||
const conversion = ((360 / tSize) * Math.PI) / 180;
|
||||
// var x1, x2, x1i, x2i, y1, y2, y1i, y2i, scale;
|
||||
for (let theta = 0; theta < tSize; theta += 1) {
|
||||
const sin = Math.sin(theta * conversion);
|
||||
const cos = Math.cos(theta * conversion);
|
||||
for (let radius = 0; radius < rSize; radius += 1) {
|
||||
x = Math.floor(xMid + ((rMax * radius) / rSize) * cos);
|
||||
y = Math.floor(yMid + ((rMax * radius) / rSize) * sin);
|
||||
let i = (y * xSize + x) * 4;
|
||||
const r = srcPixels[i + 0];
|
||||
const g = srcPixels[i + 1];
|
||||
const b = srcPixels[i + 2];
|
||||
const a = srcPixels[i + 3];
|
||||
// Store it
|
||||
//i = (theta * xSize + radius) * 4;
|
||||
i = (theta + radius * xSize) * 4;
|
||||
dstPixels[i + 0] = r;
|
||||
dstPixels[i + 1] = g;
|
||||
dstPixels[i + 2] = b;
|
||||
dstPixels[i + 3] = a;
|
||||
}
|
||||
}
|
||||
};
|
||||
/*
|
||||
* FromPolar Filter. Converts image data from polar coordinates back to rectangular.
|
||||
* Performs w*h*4 pixel reads and w*h pixel writes.
|
||||
* @function
|
||||
* @author ippo615
|
||||
* @memberof Konva.Filters
|
||||
* @param {ImageData} src, the source image data (what will be transformed)
|
||||
* @param {ImageData} dst, the destination image data (where it will be saved)
|
||||
* @param {Object} opt
|
||||
* @param {Number} [opt.polarCenterX] horizontal location for the center of the circle,
|
||||
* default is in the middle
|
||||
* @param {Number} [opt.polarCenterY] vertical location for the center of the circle,
|
||||
* default is in the middle
|
||||
* @param {Number} [opt.polarRotation] amount to rotate the image counterclockwis,
|
||||
* 0 is no rotation, 360 degrees is a full rotation
|
||||
*/
|
||||
const FromPolar = function (src, dst, opt) {
|
||||
const srcPixels = src.data, dstPixels = dst.data, xSize = src.width, ySize = src.height, xMid = opt.polarCenterX || xSize / 2, yMid = opt.polarCenterY || ySize / 2;
|
||||
// Find the largest radius
|
||||
let rMax = Math.sqrt(xMid * xMid + yMid * yMid);
|
||||
let x = xSize - xMid;
|
||||
let y = ySize - yMid;
|
||||
const rad = Math.sqrt(x * x + y * y);
|
||||
rMax = rad > rMax ? rad : rMax;
|
||||
// We'll be uisng x as the radius, and y as the angle (theta=t)
|
||||
const rSize = ySize, tSize = xSize, phaseShift = opt.polarRotation || 0;
|
||||
// We need to convert to degrees and we need to make sure
|
||||
// it's between (0-360)
|
||||
// var conversion = tSize/360*180/Math.PI;
|
||||
//var conversion = tSize/360*180/Math.PI;
|
||||
let x1, y1;
|
||||
for (x = 0; x < xSize; x += 1) {
|
||||
for (y = 0; y < ySize; y += 1) {
|
||||
const dx = x - xMid;
|
||||
const dy = y - yMid;
|
||||
const radius = (Math.sqrt(dx * dx + dy * dy) * rSize) / rMax;
|
||||
let theta = ((Math.atan2(dy, dx) * 180) / Math.PI + 360 + phaseShift) % 360;
|
||||
theta = (theta * tSize) / 360;
|
||||
x1 = Math.floor(theta);
|
||||
y1 = Math.floor(radius);
|
||||
let i = (y1 * xSize + x1) * 4;
|
||||
const r = srcPixels[i + 0];
|
||||
const g = srcPixels[i + 1];
|
||||
const b = srcPixels[i + 2];
|
||||
const a = srcPixels[i + 3];
|
||||
// Store it
|
||||
i = (y * xSize + x) * 4;
|
||||
dstPixels[i + 0] = r;
|
||||
dstPixels[i + 1] = g;
|
||||
dstPixels[i + 2] = b;
|
||||
dstPixels[i + 3] = a;
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Kaleidoscope Filter.
|
||||
* @function
|
||||
* @name Kaleidoscope
|
||||
* @author ippo615
|
||||
* @memberof Konva.Filters
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Kaleidoscope]);
|
||||
* node.kaleidoscopePower(3);
|
||||
* node.kaleidoscopeAngle(45);
|
||||
*/
|
||||
export const Kaleidoscope = function (imageData) {
|
||||
const xSize = imageData.width, ySize = imageData.height;
|
||||
let x, y, xoff, i, r, g, b, a, srcPos, dstPos;
|
||||
let power = Math.round(this.kaleidoscopePower());
|
||||
const angle = Math.round(this.kaleidoscopeAngle());
|
||||
const offset = Math.floor((xSize * (((angle % 360) + 360) % 360)) / 360);
|
||||
if (power < 1) {
|
||||
return;
|
||||
}
|
||||
const scratchData = {
|
||||
width: xSize,
|
||||
height: ySize,
|
||||
data: new Uint8ClampedArray(xSize * ySize * 4),
|
||||
};
|
||||
// Convert the original to polar coordinates.
|
||||
ToPolar(imageData, scratchData, {
|
||||
polarCenterX: xSize / 2,
|
||||
polarCenterY: ySize / 2,
|
||||
});
|
||||
// Determine how big each section will be, if it's too small
|
||||
// make it bigger
|
||||
let minSectionSize = xSize / Math.pow(2, power);
|
||||
while (minSectionSize <= 8) {
|
||||
minSectionSize = minSectionSize * 2;
|
||||
power -= 1;
|
||||
}
|
||||
minSectionSize = Math.ceil(minSectionSize);
|
||||
let sectionSize = minSectionSize;
|
||||
// Copy the offset region to 0
|
||||
// Depending on the size of filter and location of the offset we may need
|
||||
// to copy the section backwards to prevent it from rewriting itself
|
||||
let xStart = 0, xEnd = sectionSize, xDelta = 1;
|
||||
if (offset + minSectionSize > xSize) {
|
||||
xStart = sectionSize;
|
||||
xEnd = 0;
|
||||
xDelta = -1;
|
||||
}
|
||||
for (y = 0; y < ySize; y += 1) {
|
||||
for (x = xStart; x !== xEnd; x += xDelta) {
|
||||
xoff = Math.round(x + offset) % xSize;
|
||||
srcPos = (xSize * y + xoff) * 4;
|
||||
r = scratchData.data[srcPos + 0];
|
||||
g = scratchData.data[srcPos + 1];
|
||||
b = scratchData.data[srcPos + 2];
|
||||
a = scratchData.data[srcPos + 3];
|
||||
dstPos = (xSize * y + x) * 4;
|
||||
scratchData.data[dstPos + 0] = r;
|
||||
scratchData.data[dstPos + 1] = g;
|
||||
scratchData.data[dstPos + 2] = b;
|
||||
scratchData.data[dstPos + 3] = a;
|
||||
}
|
||||
}
|
||||
// Perform the actual effect
|
||||
for (y = 0; y < ySize; y += 1) {
|
||||
sectionSize = Math.floor(minSectionSize);
|
||||
for (i = 0; i < power; i += 1) {
|
||||
for (x = 0; x < sectionSize + 1; x += 1) {
|
||||
srcPos = (xSize * y + x) * 4;
|
||||
r = scratchData.data[srcPos + 0];
|
||||
g = scratchData.data[srcPos + 1];
|
||||
b = scratchData.data[srcPos + 2];
|
||||
a = scratchData.data[srcPos + 3];
|
||||
dstPos = (xSize * y + sectionSize * 2 - x - 1) * 4;
|
||||
scratchData.data[dstPos + 0] = r;
|
||||
scratchData.data[dstPos + 1] = g;
|
||||
scratchData.data[dstPos + 2] = b;
|
||||
scratchData.data[dstPos + 3] = a;
|
||||
}
|
||||
sectionSize *= 2;
|
||||
}
|
||||
}
|
||||
// Convert back from polar coordinates
|
||||
FromPolar(scratchData, imageData, { polarRotation: 0 });
|
||||
};
|
||||
/**
|
||||
* get/set kaleidoscope power. Use with {@link Konva.Filters.Kaleidoscope} filter.
|
||||
* @name Konva.Node#kaleidoscopePower
|
||||
* @method
|
||||
* @param {Integer} power of kaleidoscope
|
||||
* @returns {Integer}
|
||||
*/
|
||||
Factory.addGetterSetter(Node, 'kaleidoscopePower', 2, getNumberValidator(), Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set kaleidoscope angle. Use with {@link Konva.Filters.Kaleidoscope} filter.
|
||||
* @name Konva.Node#kaleidoscopeAngle
|
||||
* @method
|
||||
* @param {Integer} degrees
|
||||
* @returns {Integer}
|
||||
*/
|
||||
Factory.addGetterSetter(Node, 'kaleidoscopeAngle', 0, getNumberValidator(), Factory.afterSetFilter);
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
/**
|
||||
* Mask Filter
|
||||
* @function
|
||||
* @name Mask
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Mask]);
|
||||
* node.threshold(200);
|
||||
*/
|
||||
export declare const Mask: Filter;
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Node } from "../Node.js";
|
||||
import { getNumberValidator } from "../Validators.js";
|
||||
function pixelAt(idata, x, y) {
|
||||
let idx = (y * idata.width + x) * 4;
|
||||
const d = [];
|
||||
d.push(idata.data[idx++], idata.data[idx++], idata.data[idx++], idata.data[idx++]);
|
||||
return d;
|
||||
}
|
||||
function rgbDistance(p1, p2) {
|
||||
return Math.sqrt(Math.pow(p1[0] - p2[0], 2) +
|
||||
Math.pow(p1[1] - p2[1], 2) +
|
||||
Math.pow(p1[2] - p2[2], 2));
|
||||
}
|
||||
function rgbMean(pTab) {
|
||||
const m = [0, 0, 0];
|
||||
for (let i = 0; i < pTab.length; i++) {
|
||||
m[0] += pTab[i][0];
|
||||
m[1] += pTab[i][1];
|
||||
m[2] += pTab[i][2];
|
||||
}
|
||||
m[0] /= pTab.length;
|
||||
m[1] /= pTab.length;
|
||||
m[2] /= pTab.length;
|
||||
return m;
|
||||
}
|
||||
function backgroundMask(idata, threshold) {
|
||||
const rgbv_no = pixelAt(idata, 0, 0);
|
||||
const rgbv_ne = pixelAt(idata, idata.width - 1, 0);
|
||||
const rgbv_so = pixelAt(idata, 0, idata.height - 1);
|
||||
const rgbv_se = pixelAt(idata, idata.width - 1, idata.height - 1);
|
||||
if (rgbDistance(rgbv_no, rgbv_ne) < threshold &&
|
||||
rgbDistance(rgbv_ne, rgbv_se) < threshold &&
|
||||
rgbDistance(rgbv_se, rgbv_so) < threshold &&
|
||||
rgbDistance(rgbv_so, rgbv_no) < threshold) {
|
||||
// Mean color
|
||||
const mean = rgbMean([rgbv_ne, rgbv_no, rgbv_se, rgbv_so]);
|
||||
// Mask based on color distance
|
||||
const mask = [];
|
||||
for (let i = 0; i < idata.width * idata.height; i++) {
|
||||
const d = rgbDistance(mean, [
|
||||
idata.data[i * 4],
|
||||
idata.data[i * 4 + 1],
|
||||
idata.data[i * 4 + 2],
|
||||
]);
|
||||
mask[i] = d < threshold ? 0 : 255;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
}
|
||||
function applyMask(idata, mask) {
|
||||
for (let i = 0; i < idata.width * idata.height; i++) {
|
||||
idata.data[4 * i + 3] = mask[i];
|
||||
}
|
||||
}
|
||||
function erodeMask(mask, sw, sh) {
|
||||
const weights = [1, 1, 1, 1, 0, 1, 1, 1, 1];
|
||||
const side = Math.round(Math.sqrt(weights.length));
|
||||
const halfSide = Math.floor(side / 2);
|
||||
const maskResult = [];
|
||||
for (let y = 0; y < sh; y++) {
|
||||
for (let x = 0; x < sw; x++) {
|
||||
const so = y * sw + x;
|
||||
let a = 0;
|
||||
for (let cy = 0; cy < side; cy++) {
|
||||
for (let cx = 0; cx < side; cx++) {
|
||||
const scy = y + cy - halfSide;
|
||||
const scx = x + cx - halfSide;
|
||||
if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
|
||||
const srcOff = scy * sw + scx;
|
||||
const wt = weights[cy * side + cx];
|
||||
a += mask[srcOff] * wt;
|
||||
}
|
||||
}
|
||||
}
|
||||
maskResult[so] = a === 255 * 8 ? 255 : 0;
|
||||
}
|
||||
}
|
||||
return maskResult;
|
||||
}
|
||||
function dilateMask(mask, sw, sh) {
|
||||
const weights = [1, 1, 1, 1, 1, 1, 1, 1, 1];
|
||||
const side = Math.round(Math.sqrt(weights.length));
|
||||
const halfSide = Math.floor(side / 2);
|
||||
const maskResult = [];
|
||||
for (let y = 0; y < sh; y++) {
|
||||
for (let x = 0; x < sw; x++) {
|
||||
const so = y * sw + x;
|
||||
let a = 0;
|
||||
for (let cy = 0; cy < side; cy++) {
|
||||
for (let cx = 0; cx < side; cx++) {
|
||||
const scy = y + cy - halfSide;
|
||||
const scx = x + cx - halfSide;
|
||||
if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
|
||||
const srcOff = scy * sw + scx;
|
||||
const wt = weights[cy * side + cx];
|
||||
a += mask[srcOff] * wt;
|
||||
}
|
||||
}
|
||||
}
|
||||
maskResult[so] = a >= 255 * 4 ? 255 : 0;
|
||||
}
|
||||
}
|
||||
return maskResult;
|
||||
}
|
||||
function smoothEdgeMask(mask, sw, sh) {
|
||||
const weights = [
|
||||
1 / 9,
|
||||
1 / 9,
|
||||
1 / 9,
|
||||
1 / 9,
|
||||
1 / 9,
|
||||
1 / 9,
|
||||
1 / 9,
|
||||
1 / 9,
|
||||
1 / 9,
|
||||
];
|
||||
const side = Math.round(Math.sqrt(weights.length));
|
||||
const halfSide = Math.floor(side / 2);
|
||||
const maskResult = [];
|
||||
for (let y = 0; y < sh; y++) {
|
||||
for (let x = 0; x < sw; x++) {
|
||||
const so = y * sw + x;
|
||||
let a = 0;
|
||||
for (let cy = 0; cy < side; cy++) {
|
||||
for (let cx = 0; cx < side; cx++) {
|
||||
const scy = y + cy - halfSide;
|
||||
const scx = x + cx - halfSide;
|
||||
if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
|
||||
const srcOff = scy * sw + scx;
|
||||
const wt = weights[cy * side + cx];
|
||||
a += mask[srcOff] * wt;
|
||||
}
|
||||
}
|
||||
}
|
||||
maskResult[so] = a;
|
||||
}
|
||||
}
|
||||
return maskResult;
|
||||
}
|
||||
/**
|
||||
* Mask Filter
|
||||
* @function
|
||||
* @name Mask
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Mask]);
|
||||
* node.threshold(200);
|
||||
*/
|
||||
export const Mask = function (imageData) {
|
||||
var _a;
|
||||
// Detect pixels close to the background color
|
||||
const threshold = (_a = this.attrs.threshold) !== null && _a !== void 0 ? _a : 10;
|
||||
let mask = backgroundMask(imageData, threshold);
|
||||
if (mask) {
|
||||
// Erode
|
||||
mask = erodeMask(mask, imageData.width, imageData.height);
|
||||
// Dilate
|
||||
mask = dilateMask(mask, imageData.width, imageData.height);
|
||||
// Gradient
|
||||
mask = smoothEdgeMask(mask, imageData.width, imageData.height);
|
||||
// Apply mask
|
||||
applyMask(imageData, mask);
|
||||
}
|
||||
return imageData;
|
||||
};
|
||||
Factory.addGetterSetter(Node, 'threshold', 0.5, getNumberValidator(), Factory.afterSetFilter);
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
/**
|
||||
* Noise Filter. Randomly adds or substracts to the color channels
|
||||
* @function
|
||||
* @name Noise
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @author ippo615
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Noise]);
|
||||
* node.noise(0.8);
|
||||
*/
|
||||
export declare const Noise: Filter;
|
||||
/**
|
||||
* get/set noise amount. Must be a value between 0 and 1. Use with {@link Konva.Filters.Noise} filter.
|
||||
* @name Konva.Node#noise
|
||||
* @method
|
||||
* @param {Number} noise
|
||||
* @returns {Number}
|
||||
*/
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Node } from "../Node.js";
|
||||
import { getNumberValidator } from "../Validators.js";
|
||||
/**
|
||||
* Noise Filter. Randomly adds or substracts to the color channels
|
||||
* @function
|
||||
* @name Noise
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @author ippo615
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Noise]);
|
||||
* node.noise(0.8);
|
||||
*/
|
||||
export const Noise = function (imageData) {
|
||||
const amount = this.noise() * 255, data = imageData.data, nPixels = data.length, half = amount / 2;
|
||||
for (let i = 0; i < nPixels; i += 4) {
|
||||
data[i + 0] += half - 2 * half * Math.random();
|
||||
data[i + 1] += half - 2 * half * Math.random();
|
||||
data[i + 2] += half - 2 * half * Math.random();
|
||||
}
|
||||
};
|
||||
Factory.addGetterSetter(Node, 'noise', 0.2, getNumberValidator(), Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set noise amount. Must be a value between 0 and 1. Use with {@link Konva.Filters.Noise} filter.
|
||||
* @name Konva.Node#noise
|
||||
* @method
|
||||
* @param {Number} noise
|
||||
* @returns {Number}
|
||||
*/
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
/**
|
||||
* Pixelate Filter. Averages groups of pixels and redraws
|
||||
* them as larger pixels
|
||||
* @function
|
||||
* @name Pixelate
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @author ippo615
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Pixelate]);
|
||||
* node.pixelSize(10);
|
||||
*/
|
||||
export declare const Pixelate: Filter;
|
||||
/**
|
||||
* get/set pixel size. Use with {@link Konva.Filters.Pixelate} filter.
|
||||
* @name Konva.Node#pixelSize
|
||||
* @method
|
||||
* @param {Integer} pixelSize
|
||||
* @returns {Integer}
|
||||
*/
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Util } from "../Util.js";
|
||||
import { Node } from "../Node.js";
|
||||
import { getNumberValidator } from "../Validators.js";
|
||||
/**
|
||||
* Pixelate Filter. Averages groups of pixels and redraws
|
||||
* them as larger pixels
|
||||
* @function
|
||||
* @name Pixelate
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @author ippo615
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Pixelate]);
|
||||
* node.pixelSize(10);
|
||||
*/
|
||||
export const Pixelate = function (imageData, pixelRatio = 1) {
|
||||
let pixelSize = Math.ceil(this.pixelSize() * pixelRatio), width = imageData.width, height = imageData.height, nBinsX = Math.ceil(width / pixelSize), nBinsY = Math.ceil(height / pixelSize), data = imageData.data;
|
||||
if (pixelSize <= 0) {
|
||||
Util.error('pixelSize value can not be <= 0');
|
||||
return;
|
||||
}
|
||||
for (let xBin = 0; xBin < nBinsX; xBin += 1) {
|
||||
for (let yBin = 0; yBin < nBinsY; yBin += 1) {
|
||||
// Initialize the color accumlators to 0
|
||||
let red = 0;
|
||||
let green = 0;
|
||||
let blue = 0;
|
||||
let alpha = 0;
|
||||
// Determine which pixels are included in this bin
|
||||
const xBinStart = xBin * pixelSize;
|
||||
const xBinEnd = Math.min(xBinStart + pixelSize, width);
|
||||
const yBinStart = yBin * pixelSize;
|
||||
const yBinEnd = Math.min(yBinStart + pixelSize, height);
|
||||
// Add all of the pixels to this bin!
|
||||
for (let x = xBinStart; x < xBinEnd; x += 1) {
|
||||
for (let y = yBinStart; y < yBinEnd; y += 1) {
|
||||
const i = (width * y + x) * 4;
|
||||
const a = data[i + 3];
|
||||
red += data[i + 0] * a;
|
||||
green += data[i + 1] * a;
|
||||
blue += data[i + 2] * a;
|
||||
alpha += a;
|
||||
}
|
||||
}
|
||||
// average premultiplied colour so transparent pixels do not darken the block
|
||||
if (alpha) {
|
||||
red = red / alpha;
|
||||
green = green / alpha;
|
||||
blue = blue / alpha;
|
||||
}
|
||||
alpha = alpha / ((xBinEnd - xBinStart) * (yBinEnd - yBinStart));
|
||||
// Draw this bin
|
||||
for (let x = xBinStart; x < xBinEnd; x += 1) {
|
||||
for (let y = yBinStart; y < yBinEnd; y += 1) {
|
||||
const i = (width * y + x) * 4;
|
||||
data[i + 0] = red;
|
||||
data[i + 1] = green;
|
||||
data[i + 2] = blue;
|
||||
data[i + 3] = alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Factory.addGetterSetter(Node, 'pixelSize', 8, getNumberValidator(), Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set pixel size. Use with {@link Konva.Filters.Pixelate} filter.
|
||||
* @name Konva.Node#pixelSize
|
||||
* @method
|
||||
* @param {Integer} pixelSize
|
||||
* @returns {Integer}
|
||||
*/
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
/**
|
||||
* Posterize Filter. Adjusts the channels so that there are no more
|
||||
* than n different values for that channel. This is also applied
|
||||
* to the alpha channel.
|
||||
* @function
|
||||
* @name Posterize
|
||||
* @author ippo615
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Posterize]);
|
||||
* node.levels(0.8); // between 0 and 1
|
||||
*/
|
||||
export declare const Posterize: Filter;
|
||||
/**
|
||||
* get/set levels. Must be a number between 0 and 1. Use with {@link Konva.Filters.Posterize} filter.
|
||||
* @name Konva.Node#levels
|
||||
* @method
|
||||
* @param {Number} level between 0 and 1
|
||||
* @returns {Number}
|
||||
*/
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Node } from "../Node.js";
|
||||
import { getNumberValidator } from "../Validators.js";
|
||||
/**
|
||||
* Posterize Filter. Adjusts the channels so that there are no more
|
||||
* than n different values for that channel. This is also applied
|
||||
* to the alpha channel.
|
||||
* @function
|
||||
* @name Posterize
|
||||
* @author ippo615
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Posterize]);
|
||||
* node.levels(0.8); // between 0 and 1
|
||||
*/
|
||||
export const Posterize = function (imageData) {
|
||||
// level must be between 1 and 255
|
||||
const levels = Math.round(this.levels() * 254) + 1, data = imageData.data, len = data.length, scale = 255 / levels;
|
||||
for (let i = 0; i < len; i += 1) {
|
||||
data[i] = Math.floor(data[i] / scale) * scale;
|
||||
}
|
||||
};
|
||||
Factory.addGetterSetter(Node, 'levels', 0.5, getNumberValidator(), Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set levels. Must be a number between 0 and 1. Use with {@link Konva.Filters.Posterize} filter.
|
||||
* @name Konva.Node#levels
|
||||
* @method
|
||||
* @param {Number} level between 0 and 1
|
||||
* @returns {Number}
|
||||
*/
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
/**
|
||||
* RGB Filter
|
||||
* @function
|
||||
* @name RGB
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @author ippo615
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.RGB]);
|
||||
* node.blue(120);
|
||||
* node.green(200);
|
||||
*/
|
||||
export declare const RGB: Filter;
|
||||
/**
|
||||
* get/set filter blue value. Use with {@link Konva.Filters.RGB} or {@link Konva.Filters.RGBA} filter.
|
||||
* @name blue
|
||||
* @method
|
||||
* @memberof Konva.Node.prototype
|
||||
* @param {Integer} blue value between 0 and 255
|
||||
* @returns {Integer}
|
||||
*/
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Node } from "../Node.js";
|
||||
import { RGBComponent } from "../Validators.js";
|
||||
/**
|
||||
* RGB Filter
|
||||
* @function
|
||||
* @name RGB
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @author ippo615
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.RGB]);
|
||||
* node.blue(120);
|
||||
* node.green(200);
|
||||
*/
|
||||
export const RGB = function (imageData) {
|
||||
const data = imageData.data, nPixels = data.length, red = this.red(), green = this.green(), blue = this.blue();
|
||||
for (let i = 0; i < nPixels; i += 4) {
|
||||
const brightness = (0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2]) / 255;
|
||||
data[i] = brightness * red; // r
|
||||
data[i + 1] = brightness * green; // g
|
||||
data[i + 2] = brightness * blue; // b
|
||||
data[i + 3] = data[i + 3]; // alpha
|
||||
}
|
||||
};
|
||||
Factory.addGetterSetter(Node, 'red', 0, RGBComponent, Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set filter red value. Use with {@link Konva.Filters.RGB} or {@link Konva.Filters.RGBA} filter.
|
||||
* @name red
|
||||
* @method
|
||||
* @memberof Konva.Node.prototype
|
||||
* @param {Integer} red value between 0 and 255
|
||||
* @returns {Integer}
|
||||
*/
|
||||
Factory.addGetterSetter(Node, 'green', 0, RGBComponent, Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set filter green value. Use with {@link Konva.Filters.RGB} or {@link Konva.Filters.RGBA} filter.
|
||||
* @name green
|
||||
* @method
|
||||
* @memberof Konva.Node.prototype
|
||||
* @param {Integer} green value between 0 and 255
|
||||
* @returns {Integer}
|
||||
*/
|
||||
Factory.addGetterSetter(Node, 'blue', 0, RGBComponent, Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set filter blue value. Use with {@link Konva.Filters.RGB} or {@link Konva.Filters.RGBA} filter.
|
||||
* @name blue
|
||||
* @method
|
||||
* @memberof Konva.Node.prototype
|
||||
* @param {Integer} blue value between 0 and 255
|
||||
* @returns {Integer}
|
||||
*/
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
import './RGB.ts';
|
||||
/**
|
||||
* RGBA Filter
|
||||
* @function
|
||||
* @name RGBA
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @author codefo
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.RGBA]);
|
||||
* node.blue(120);
|
||||
* node.green(200);
|
||||
* node.alpha(0.3);
|
||||
*/
|
||||
export declare const RGBA: Filter;
|
||||
/**
|
||||
* get/set filter alpha value. Use with {@link Konva.Filters.RGBA} filter.
|
||||
* @name alpha
|
||||
* @method
|
||||
* @memberof Konva.Node.prototype
|
||||
* @param {Float} alpha value between 0 and 1
|
||||
* @returns {Float}
|
||||
*/
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Node } from "../Node.js";
|
||||
// red, green and blue are registered there
|
||||
import "./RGB.js";
|
||||
/**
|
||||
* RGBA Filter
|
||||
* @function
|
||||
* @name RGBA
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @author codefo
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.RGBA]);
|
||||
* node.blue(120);
|
||||
* node.green(200);
|
||||
* node.alpha(0.3);
|
||||
*/
|
||||
export const RGBA = function (imageData) {
|
||||
const data = imageData.data, nPixels = data.length, red = this.red(), green = this.green(), blue = this.blue(), alpha = this.alpha();
|
||||
for (let i = 0; i < nPixels; i += 4) {
|
||||
const ia = 1 - alpha;
|
||||
data[i] = red * alpha + data[i] * ia; // r
|
||||
data[i + 1] = green * alpha + data[i + 1] * ia; // g
|
||||
data[i + 2] = blue * alpha + data[i + 2] * ia; // b
|
||||
}
|
||||
};
|
||||
Factory.addGetterSetter(Node, 'alpha', 1, (val) => Math.min(1, Math.max(0, val)), Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set filter alpha value. Use with {@link Konva.Filters.RGBA} filter.
|
||||
* @name alpha
|
||||
* @method
|
||||
* @memberof Konva.Node.prototype
|
||||
* @param {Float} alpha value between 0 and 1
|
||||
* @returns {Float}
|
||||
*/
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
/**
|
||||
* @function
|
||||
* @name Sepia
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Sepia]);
|
||||
*/
|
||||
export declare const Sepia: Filter;
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// based on https://stackoverflow.com/questions/1061093/how-is-a-sepia-tone-created
|
||||
/**
|
||||
* @function
|
||||
* @name Sepia
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Sepia]);
|
||||
*/
|
||||
export const Sepia = function (imageData) {
|
||||
const data = imageData.data, nPixels = data.length;
|
||||
for (let i = 0; i < nPixels; i += 4) {
|
||||
const r = data[i + 0];
|
||||
const g = data[i + 1];
|
||||
const b = data[i + 2];
|
||||
data[i + 0] = Math.min(255, r * 0.393 + g * 0.769 + b * 0.189);
|
||||
data[i + 1] = Math.min(255, r * 0.349 + g * 0.686 + b * 0.168);
|
||||
data[i + 2] = Math.min(255, r * 0.272 + g * 0.534 + b * 0.131);
|
||||
}
|
||||
};
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
/**
|
||||
* Solarize Filter
|
||||
* @function
|
||||
* @name Solarize
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Solarize]);
|
||||
*/
|
||||
export declare const Solarize: Filter;
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Solarize Filter
|
||||
* @function
|
||||
* @name Solarize
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Solarize]);
|
||||
*/
|
||||
export const Solarize = function (imageData) {
|
||||
const threshold = 128;
|
||||
const d = imageData.data;
|
||||
for (let i = 0; i < d.length; i += 4) {
|
||||
const r = d[i], g = d[i + 1], b = d[i + 2];
|
||||
// sRGB luma
|
||||
const L = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||||
if (L >= threshold) {
|
||||
d[i] = 255 - r;
|
||||
d[i + 1] = 255 - g;
|
||||
d[i + 2] = 255 - b;
|
||||
}
|
||||
}
|
||||
return imageData;
|
||||
};
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import type { Filter } from '../Node.ts';
|
||||
/**
|
||||
* Threshold Filter. Pushes any value above the mid point to
|
||||
* the max and any value below the mid point to the min.
|
||||
* This affects the alpha channel.
|
||||
* @function
|
||||
* @name Threshold
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @author ippo615
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Threshold]);
|
||||
* node.threshold(0.1);
|
||||
*/
|
||||
export declare const Threshold: Filter;
|
||||
/**
|
||||
* get/set threshold. With {@link Konva.Filters.Threshold} it is a value between 0 and 1, default 0.5. With {@link Konva.Filters.Mask} it is the colour distance to the background, 0 to 255, default 10.
|
||||
* @name threshold
|
||||
* @method
|
||||
* @memberof Konva.Node.prototype
|
||||
* @param {Number} threshold
|
||||
* @returns {Number}
|
||||
*/
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Node } from "../Node.js";
|
||||
import { getNumberValidator } from "../Validators.js";
|
||||
/**
|
||||
* Threshold Filter. Pushes any value above the mid point to
|
||||
* the max and any value below the mid point to the min.
|
||||
* This affects the alpha channel.
|
||||
* @function
|
||||
* @name Threshold
|
||||
* @memberof Konva.Filters
|
||||
* @param {Object} imageData
|
||||
* @author ippo615
|
||||
* @example
|
||||
* node.cache();
|
||||
* node.filters([Konva.Filters.Threshold]);
|
||||
* node.threshold(0.1);
|
||||
*/
|
||||
export const Threshold = function (imageData) {
|
||||
const level = this.threshold() * 255, data = imageData.data, len = data.length;
|
||||
for (let i = 0; i < len; i += 1) {
|
||||
data[i] = data[i] < level ? 0 : 255;
|
||||
}
|
||||
};
|
||||
Factory.addGetterSetter(Node, 'threshold', 0.5, getNumberValidator(), Factory.afterSetFilter);
|
||||
/**
|
||||
* get/set threshold. With {@link Konva.Filters.Threshold} it is a value between 0 and 1, default 0.5. With {@link Konva.Filters.Mask} it is the colour distance to the background, 0 to 255, default 10.
|
||||
* @name threshold
|
||||
* @method
|
||||
* @memberof Konva.Node.prototype
|
||||
* @param {Number} threshold
|
||||
* @returns {Number}
|
||||
*/
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
import { Konva } from './_FullInternals.ts';
|
||||
export default Konva;
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
import { Konva } from "./_FullInternals.js";
|
||||
export default Konva;
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
import type { ShapeConfig } from '../Shape.ts';
|
||||
import { Shape } from '../Shape.ts';
|
||||
import type { GetSet } from '../types.ts';
|
||||
import type { Context } from '../Context.ts';
|
||||
export interface ArcConfig extends ShapeConfig {
|
||||
angle: number;
|
||||
innerRadius: number;
|
||||
outerRadius: number;
|
||||
clockwise?: boolean;
|
||||
}
|
||||
/**
|
||||
* Arc constructor
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Shape
|
||||
* @param {Object} config
|
||||
* @param {Number} config.angle in degrees
|
||||
* @param {Number} config.innerRadius
|
||||
* @param {Number} config.outerRadius
|
||||
* @param {Boolean} [config.clockwise]
|
||||
* @@shapeParams
|
||||
* @@nodeParams
|
||||
* @example
|
||||
* // draw a Arc that's pointing downwards
|
||||
* var arc = new Konva.Arc({
|
||||
* innerRadius: 40,
|
||||
* outerRadius: 80,
|
||||
* fill: 'red',
|
||||
* stroke: 'black',
|
||||
* strokeWidth: 5,
|
||||
* angle: 60,
|
||||
* rotation: -120
|
||||
* });
|
||||
*/
|
||||
export declare class Arc extends Shape<ArcConfig> {
|
||||
_sceneFunc(context: Context): void;
|
||||
getWidth(): number;
|
||||
getHeight(): number;
|
||||
setWidth(width: number): void;
|
||||
setHeight(height: number): void;
|
||||
getSelfRect(): {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
innerRadius: GetSet<number, this>;
|
||||
outerRadius: GetSet<number, this>;
|
||||
angle: GetSet<number, this>;
|
||||
clockwise: GetSet<boolean, this>;
|
||||
}
|
||||
/**
|
||||
* get/set clockwise flag
|
||||
* @name Konva.Arc#clockwise
|
||||
* @method
|
||||
* @param {Boolean} clockwise
|
||||
* @returns {Boolean}
|
||||
* @example
|
||||
* // get clockwise flag
|
||||
* var clockwise = arc.clockwise();
|
||||
*
|
||||
* // draw arc counter-clockwise
|
||||
* arc.clockwise(false);
|
||||
*
|
||||
* // draw arc clockwise
|
||||
* arc.clockwise(true);
|
||||
*/
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Shape } from "../Shape.js";
|
||||
import { Konva } from "../Global.js";
|
||||
import { getNumberValidator, getBooleanValidator } from "../Validators.js";
|
||||
import { _registerNode } from "../Global.js";
|
||||
/**
|
||||
* Arc constructor
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Shape
|
||||
* @param {Object} config
|
||||
* @param {Number} config.angle in degrees
|
||||
* @param {Number} config.innerRadius
|
||||
* @param {Number} config.outerRadius
|
||||
* @param {Boolean} [config.clockwise]
|
||||
* @@shapeParams
|
||||
* @@nodeParams
|
||||
* @example
|
||||
* // draw a Arc that's pointing downwards
|
||||
* var arc = new Konva.Arc({
|
||||
* innerRadius: 40,
|
||||
* outerRadius: 80,
|
||||
* fill: 'red',
|
||||
* stroke: 'black',
|
||||
* strokeWidth: 5,
|
||||
* angle: 60,
|
||||
* rotation: -120
|
||||
* });
|
||||
*/
|
||||
export class Arc extends Shape {
|
||||
_sceneFunc(context) {
|
||||
const angle = Konva.getAngle(this.angle()), clockwise = this.clockwise();
|
||||
context.beginPath();
|
||||
context.arc(0, 0, Math.abs(this.outerRadius()), 0, angle, clockwise);
|
||||
context.arc(0, 0, Math.abs(this.innerRadius()), angle, 0, !clockwise);
|
||||
context.closePath();
|
||||
context.fillStrokeShape(this);
|
||||
}
|
||||
getWidth() {
|
||||
return Math.abs(this.outerRadius()) * 2;
|
||||
}
|
||||
getHeight() {
|
||||
return Math.abs(this.outerRadius()) * 2;
|
||||
}
|
||||
setWidth(width) {
|
||||
this.outerRadius(width / 2);
|
||||
}
|
||||
setHeight(height) {
|
||||
this.outerRadius(height / 2);
|
||||
}
|
||||
getSelfRect() {
|
||||
const r1 = Math.abs(this.innerRadius()), r2 = Math.abs(this.outerRadius());
|
||||
const innerRadius = Math.min(r1, r2), outerRadius = Math.max(r1, r2);
|
||||
const clockwise = this.clockwise();
|
||||
// canvas draws a full circle for any non-zero full-turn end angle regardless
|
||||
// of direction, and nothing for angle 0. Keep exact turns distinct from
|
||||
// the modulo-normalized partial sweep below.
|
||||
const rawAngle = Konva.getAngle(this.angle());
|
||||
if (rawAngle % (Math.PI * 2) === 0) {
|
||||
return rawAngle !== 0
|
||||
? {
|
||||
x: -outerRadius,
|
||||
y: -outerRadius,
|
||||
width: outerRadius * 2,
|
||||
height: outerRadius * 2,
|
||||
}
|
||||
: { x: innerRadius, y: 0, width: outerRadius - innerRadius, height: 0 };
|
||||
}
|
||||
const turn = Math.PI * 2;
|
||||
const sweep = clockwise ? -rawAngle : rawAngle;
|
||||
const angle = sweep >= turn ? turn : ((sweep % turn) + turn) % turn;
|
||||
const boundLeftRatio = Math.cos(Math.min(angle, Math.PI));
|
||||
const boundRightRatio = 1;
|
||||
const boundTopRatio = Math.sin(Math.min(Math.max(Math.PI, angle), (3 * Math.PI) / 2));
|
||||
const boundBottomRatio = Math.sin(Math.min(angle, Math.PI / 2));
|
||||
const boundLeft = boundLeftRatio * (boundLeftRatio > 0 ? innerRadius : outerRadius);
|
||||
const boundRight = boundRightRatio * (boundRightRatio > 0 ? outerRadius : innerRadius);
|
||||
const boundTop = boundTopRatio * (boundTopRatio > 0 ? innerRadius : outerRadius);
|
||||
const boundBottom = boundBottomRatio * (boundBottomRatio > 0 ? outerRadius : innerRadius);
|
||||
return {
|
||||
x: boundLeft,
|
||||
y: clockwise ? -1 * boundBottom : boundTop,
|
||||
width: boundRight - boundLeft,
|
||||
height: boundBottom - boundTop,
|
||||
};
|
||||
}
|
||||
}
|
||||
Arc.prototype._centroid = true;
|
||||
Arc.prototype.className = 'Arc';
|
||||
Arc.prototype._attrsAffectingSize = [
|
||||
'innerRadius',
|
||||
'outerRadius',
|
||||
'angle',
|
||||
'clockwise',
|
||||
];
|
||||
_registerNode(Arc, true);
|
||||
// add getters setters
|
||||
Factory.addGetterSetter(Arc, 'innerRadius', 0, getNumberValidator());
|
||||
/**
|
||||
* get/set innerRadius
|
||||
* @name Konva.Arc#innerRadius
|
||||
* @method
|
||||
* @param {Number} innerRadius
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* // get inner radius
|
||||
* var innerRadius = arc.innerRadius();
|
||||
*
|
||||
* // set inner radius
|
||||
* arc.innerRadius(20);
|
||||
*/
|
||||
Factory.addGetterSetter(Arc, 'outerRadius', 0, getNumberValidator());
|
||||
/**
|
||||
* get/set outerRadius
|
||||
* @name Konva.Arc#outerRadius
|
||||
* @method
|
||||
* @param {Number} outerRadius
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* // get outer radius
|
||||
* var outerRadius = arc.outerRadius();
|
||||
*
|
||||
* // set outer radius
|
||||
* arc.outerRadius(20);
|
||||
*/
|
||||
Factory.addGetterSetter(Arc, 'angle', 0, getNumberValidator());
|
||||
/**
|
||||
* get/set angle in degrees
|
||||
* @name Konva.Arc#angle
|
||||
* @method
|
||||
* @param {Number} angle
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* // get angle
|
||||
* var angle = arc.angle();
|
||||
*
|
||||
* // set angle
|
||||
* arc.angle(20);
|
||||
*/
|
||||
Factory.addGetterSetter(Arc, 'clockwise', false, getBooleanValidator());
|
||||
/**
|
||||
* get/set clockwise flag
|
||||
* @name Konva.Arc#clockwise
|
||||
* @method
|
||||
* @param {Boolean} clockwise
|
||||
* @returns {Boolean}
|
||||
* @example
|
||||
* // get clockwise flag
|
||||
* var clockwise = arc.clockwise();
|
||||
*
|
||||
* // draw arc counter-clockwise
|
||||
* arc.clockwise(false);
|
||||
*
|
||||
* // draw arc clockwise
|
||||
* arc.clockwise(true);
|
||||
*/
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
import type { LineConfig } from './Line.ts';
|
||||
import { Line } from './Line.ts';
|
||||
import type { GetSet } from '../types.ts';
|
||||
import type { Context } from '../Context.ts';
|
||||
export interface ArrowConfig extends LineConfig {
|
||||
tension?: number;
|
||||
closed?: boolean;
|
||||
pointerLength?: number;
|
||||
pointerWidth?: number;
|
||||
pointerAtBeginning?: boolean;
|
||||
pointerAtEnding?: boolean;
|
||||
}
|
||||
/**
|
||||
* Arrow constructor
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Line
|
||||
* @param {Object} config
|
||||
* @param {Array} config.points Flat array of points coordinates. You should define them as [x1, y1, x2, y2, x3, y3].
|
||||
* @param {Number} [config.tension] Higher values will result in a more curvy line. A value of 0 will result in no interpolation.
|
||||
* The default is 0
|
||||
* @param {Number} config.pointerLength Arrow pointer length. Default value is 10.
|
||||
* @param {Number} config.pointerWidth Arrow pointer width. Default value is 10.
|
||||
* @param {Boolean} config.pointerAtBeginning Do we need to draw pointer on beginning position?. Default false.
|
||||
* @param {Boolean} config.pointerAtEnding Do we need to draw pointer on ending position?. Default true.
|
||||
* @@shapeParams
|
||||
* @@nodeParams
|
||||
* @example
|
||||
* var line = new Konva.Line({
|
||||
* points: [73, 70, 340, 23, 450, 60, 500, 20],
|
||||
* stroke: 'red',
|
||||
* tension: 1,
|
||||
* pointerLength : 10,
|
||||
* pointerWidth : 12
|
||||
* });
|
||||
*/
|
||||
export declare class Arrow extends Line<ArrowConfig> {
|
||||
_getStrokePadding(): number;
|
||||
_sceneFunc(ctx: Context): void;
|
||||
_getPointerAngle(atBeginning: boolean): number;
|
||||
__fillStroke(ctx: Context): void;
|
||||
getSelfRect(): import("../types.ts").IRect;
|
||||
pointerLength: GetSet<number, this>;
|
||||
pointerWidth: GetSet<number, this>;
|
||||
pointerAtEnding: GetSet<boolean, this>;
|
||||
pointerAtBeginning: GetSet<boolean, this>;
|
||||
}
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Line } from "./Line.js";
|
||||
import { getNumberValidator } from "../Validators.js";
|
||||
import { _registerNode } from "../Global.js";
|
||||
import { Path } from "./Path.js";
|
||||
/**
|
||||
* Arrow constructor
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Line
|
||||
* @param {Object} config
|
||||
* @param {Array} config.points Flat array of points coordinates. You should define them as [x1, y1, x2, y2, x3, y3].
|
||||
* @param {Number} [config.tension] Higher values will result in a more curvy line. A value of 0 will result in no interpolation.
|
||||
* The default is 0
|
||||
* @param {Number} config.pointerLength Arrow pointer length. Default value is 10.
|
||||
* @param {Number} config.pointerWidth Arrow pointer width. Default value is 10.
|
||||
* @param {Boolean} config.pointerAtBeginning Do we need to draw pointer on beginning position?. Default false.
|
||||
* @param {Boolean} config.pointerAtEnding Do we need to draw pointer on ending position?. Default true.
|
||||
* @@shapeParams
|
||||
* @@nodeParams
|
||||
* @example
|
||||
* var line = new Konva.Line({
|
||||
* points: [73, 70, 340, 23, 450, 60, 500, 20],
|
||||
* stroke: 'red',
|
||||
* tension: 1,
|
||||
* pointerLength : 10,
|
||||
* pointerWidth : 12
|
||||
* });
|
||||
*/
|
||||
export class Arrow extends Line {
|
||||
_getStrokePadding() {
|
||||
// Arrowheads have joins even when the shaft is a single straight line.
|
||||
return super._getStrokePadding(this.pointerAtBeginning() || this.pointerAtEnding()
|
||||
? this.miterLimit() || 10
|
||||
: undefined);
|
||||
}
|
||||
_sceneFunc(ctx) {
|
||||
super._sceneFunc(ctx);
|
||||
const points = this.points();
|
||||
const n = points.length;
|
||||
if (n < 4)
|
||||
return;
|
||||
const length = this.pointerLength();
|
||||
const width = this.pointerWidth();
|
||||
if (this.pointerAtEnding()) {
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.translate(points[n - 2], points[n - 1]);
|
||||
ctx.rotate(this._getPointerAngle(false));
|
||||
ctx.moveTo(0, 0);
|
||||
ctx.lineTo(-length, width / 2);
|
||||
ctx.lineTo(-length, -width / 2);
|
||||
ctx.closePath();
|
||||
ctx.restore();
|
||||
this.__fillStroke(ctx);
|
||||
}
|
||||
if (this.pointerAtBeginning()) {
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.translate(points[0], points[1]);
|
||||
ctx.rotate(this._getPointerAngle(true));
|
||||
ctx.moveTo(0, 0);
|
||||
ctx.lineTo(-length, width / 2);
|
||||
ctx.lineTo(-length, -width / 2);
|
||||
ctx.closePath();
|
||||
ctx.restore();
|
||||
this.__fillStroke(ctx);
|
||||
}
|
||||
}
|
||||
_getPointerAngle(atBeginning) {
|
||||
const points = this.points();
|
||||
const n = points.length;
|
||||
const fromTension = this.tension() !== 0 && n > 4;
|
||||
const tp = fromTension ? this.getTensionPoints() : points;
|
||||
const ex = atBeginning ? points[0] : points[n - 2], ey = atBeginning ? points[1] : points[n - 1];
|
||||
let dx = 0, dy = 0;
|
||||
if (fromTension && atBeginning) {
|
||||
dx = ex - (tp[0] + tp[2]) / 2;
|
||||
dy = ey - (tp[1] + tp[3]) / 2;
|
||||
}
|
||||
else if (fromTension) {
|
||||
const x = tp[tp.length - 4], y = tp[tp.length - 3];
|
||||
const controlX = tp[tp.length - 2], controlY = tp[tp.length - 1];
|
||||
const lastLength = Path.calcLength(x, y, 'Q', [
|
||||
controlX,
|
||||
controlY,
|
||||
ex,
|
||||
ey,
|
||||
]);
|
||||
const previous = Path.getPointOnQuadraticBezier(lastLength ? Math.max(0, 1 - this.pointerLength() / lastLength) : 0, x, y, controlX, controlY, ex, ey);
|
||||
dx = ex - previous.x;
|
||||
dy = ey - previous.y;
|
||||
}
|
||||
// repeated endpoints give a zero tangent, so walk towards the other end
|
||||
// of the line until a distinct point is found
|
||||
for (let i = atBeginning ? 0 : tp.length - 2; !dx && !dy && i >= 0 && i < tp.length; i += atBeginning ? 2 : -2) {
|
||||
dx = ex - tp[i];
|
||||
dy = ey - tp[i + 1];
|
||||
}
|
||||
const turn = Math.PI * 2;
|
||||
return (Math.atan2(dy, dx) + turn) % turn;
|
||||
}
|
||||
__fillStroke(ctx) {
|
||||
// here is a tricky part
|
||||
// we need to disable dash for arrow pointers
|
||||
const isDashEnabled = this.dashEnabled();
|
||||
if (isDashEnabled) {
|
||||
// manually disable dash for head
|
||||
// it is better not to use setter here,
|
||||
// because it will trigger attr change event
|
||||
this.attrs.dashEnabled = false;
|
||||
ctx.setLineDash([]);
|
||||
}
|
||||
ctx.fillStrokeShape(this);
|
||||
// restore old value
|
||||
if (isDashEnabled) {
|
||||
this.attrs.dashEnabled = true;
|
||||
}
|
||||
}
|
||||
getSelfRect() {
|
||||
const lineRect = super.getSelfRect();
|
||||
const points = this.points();
|
||||
if (points.length < 4)
|
||||
return lineRect;
|
||||
let minX = lineRect.x, minY = lineRect.y;
|
||||
let maxX = minX + lineRect.width, maxY = minY + lineRect.height;
|
||||
for (const beginning of [false, true]) {
|
||||
if (!(beginning ? this.pointerAtBeginning() : this.pointerAtEnding()))
|
||||
continue;
|
||||
const index = beginning ? 0 : points.length - 2;
|
||||
const angle = this._getPointerAngle(beginning);
|
||||
const cos = Math.cos(angle), sin = Math.sin(angle);
|
||||
const x = points[index] - this.pointerLength() * cos;
|
||||
const y = points[index + 1] - this.pointerLength() * sin;
|
||||
const dx = Math.abs((this.pointerWidth() * sin) / 2);
|
||||
const dy = Math.abs((this.pointerWidth() * cos) / 2);
|
||||
minX = Math.min(minX, x - dx);
|
||||
minY = Math.min(minY, y - dy);
|
||||
maxX = Math.max(maxX, x + dx);
|
||||
maxY = Math.max(maxY, y + dy);
|
||||
}
|
||||
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
||||
}
|
||||
}
|
||||
Arrow.prototype.className = 'Arrow';
|
||||
Arrow.prototype._attrsAffectingSize = [
|
||||
...Line.prototype._attrsAffectingSize,
|
||||
'pointerLength',
|
||||
'pointerWidth',
|
||||
'pointerAtBeginning',
|
||||
'pointerAtEnding',
|
||||
];
|
||||
_registerNode(Arrow, true);
|
||||
/**
|
||||
* get/set pointerLength
|
||||
* @name Konva.Arrow#pointerLength
|
||||
* @method
|
||||
* @param {Number} Length of pointer of arrow. The default is 10.
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* // get length
|
||||
* var pointerLength = line.pointerLength();
|
||||
*
|
||||
* // set length
|
||||
* line.pointerLength(15);
|
||||
*/
|
||||
Factory.addGetterSetter(Arrow, 'pointerLength', 10, getNumberValidator());
|
||||
/**
|
||||
* get/set pointerWidth
|
||||
* @name Konva.Arrow#pointerWidth
|
||||
* @method
|
||||
* @param {Number} Width of pointer of arrow.
|
||||
* The default is 10.
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* // get width
|
||||
* var pointerWidth = line.pointerWidth();
|
||||
*
|
||||
* // set width
|
||||
* line.pointerWidth(15);
|
||||
*/
|
||||
Factory.addGetterSetter(Arrow, 'pointerWidth', 10, getNumberValidator());
|
||||
/**
|
||||
* get/set pointerAtBeginning
|
||||
* @name Konva.Arrow#pointerAtBeginning
|
||||
* @method
|
||||
* @param {Boolean} pointerAtBeginning Whether to draw a pointer at the beginning of the arrow. The default is false.
|
||||
* @returns {Boolean}
|
||||
* @example
|
||||
* // get value
|
||||
* var pointerAtBeginning = line.pointerAtBeginning();
|
||||
*
|
||||
* // set value
|
||||
* line.pointerAtBeginning(true);
|
||||
*/
|
||||
Factory.addGetterSetter(Arrow, 'pointerAtBeginning', false);
|
||||
/**
|
||||
* get/set pointerAtEnding
|
||||
* @name Konva.Arrow#pointerAtEnding
|
||||
* @method
|
||||
* @param {Boolean} pointerAtEnding Whether to draw a pointer at the end of the arrow. The default is true.
|
||||
* @returns {Boolean}
|
||||
* @example
|
||||
* // get value
|
||||
* var pointerAtEnding = line.pointerAtEnding();
|
||||
*
|
||||
* // set value
|
||||
* line.pointerAtEnding(false);
|
||||
*/
|
||||
Factory.addGetterSetter(Arrow, 'pointerAtEnding', true);
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import type { ShapeConfig } from '../Shape.ts';
|
||||
import { Shape } from '../Shape.ts';
|
||||
import type { GetSet } from '../types.ts';
|
||||
import type { Context } from '../Context.ts';
|
||||
export interface CircleConfig extends ShapeConfig {
|
||||
radius?: number;
|
||||
}
|
||||
/**
|
||||
* Circle constructor
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Shape
|
||||
* @param {Object} config
|
||||
* @param {Number} config.radius
|
||||
* @@shapeParams
|
||||
* @@nodeParams
|
||||
* @example
|
||||
* // create circle
|
||||
* var circle = new Konva.Circle({
|
||||
* radius: 40,
|
||||
* fill: 'red',
|
||||
* stroke: 'black',
|
||||
* strokeWidth: 5
|
||||
* });
|
||||
*/
|
||||
export declare class Circle extends Shape<CircleConfig> {
|
||||
_getStrokePadding(): number;
|
||||
_sceneFunc(context: Context): void;
|
||||
getWidth(): number;
|
||||
getHeight(): number;
|
||||
setWidth(width: number): void;
|
||||
setHeight(height: number): void;
|
||||
radius: GetSet<number, this>;
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Shape } from "../Shape.js";
|
||||
import { getNumberValidator } from "../Validators.js";
|
||||
import { _registerNode } from "../Global.js";
|
||||
/**
|
||||
* Circle constructor
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Shape
|
||||
* @param {Object} config
|
||||
* @param {Number} config.radius
|
||||
* @@shapeParams
|
||||
* @@nodeParams
|
||||
* @example
|
||||
* // create circle
|
||||
* var circle = new Konva.Circle({
|
||||
* radius: 40,
|
||||
* fill: 'red',
|
||||
* stroke: 'black',
|
||||
* strokeWidth: 5
|
||||
* });
|
||||
*/
|
||||
export class Circle extends Shape {
|
||||
_getStrokePadding() {
|
||||
return super._getStrokePadding(1);
|
||||
}
|
||||
_sceneFunc(context) {
|
||||
context.beginPath();
|
||||
context.arc(0, 0, Math.abs(this.attrs.radius || 0), 0, Math.PI * 2, false);
|
||||
context.closePath();
|
||||
context.fillStrokeShape(this);
|
||||
}
|
||||
getWidth() {
|
||||
return Math.abs(this.radius()) * 2;
|
||||
}
|
||||
getHeight() {
|
||||
return Math.abs(this.radius()) * 2;
|
||||
}
|
||||
setWidth(width) {
|
||||
this.radius(width / 2);
|
||||
}
|
||||
setHeight(height) {
|
||||
this.radius(height / 2);
|
||||
}
|
||||
}
|
||||
Circle.prototype._centroid = true;
|
||||
Circle.prototype.className = 'Circle';
|
||||
Circle.prototype._attrsAffectingSize = ['radius'];
|
||||
_registerNode(Circle, true);
|
||||
/**
|
||||
* get/set radius
|
||||
* @name Konva.Circle#radius
|
||||
* @method
|
||||
* @param {Number} radius
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* // get radius
|
||||
* var radius = circle.radius();
|
||||
*
|
||||
* // set radius
|
||||
* circle.radius(10);
|
||||
*/
|
||||
Factory.addGetterSetter(Circle, 'radius', 0, getNumberValidator());
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
import type { ShapeConfig } from '../Shape.ts';
|
||||
import { Shape } from '../Shape.ts';
|
||||
import type { Context } from '../Context.ts';
|
||||
import type { GetSet, Vector2d } from '../types.ts';
|
||||
export interface EllipseConfig extends ShapeConfig {
|
||||
radius?: Vector2d;
|
||||
radiusX?: number;
|
||||
radiusY?: number;
|
||||
}
|
||||
/**
|
||||
* Ellipse constructor
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Shape
|
||||
* @param {Object} config
|
||||
* @param {Object} config.radius defines x and y radius
|
||||
* @@shapeParams
|
||||
* @@nodeParams
|
||||
* @example
|
||||
* var ellipse = new Konva.Ellipse({
|
||||
* radius : {
|
||||
* x : 50,
|
||||
* y : 50
|
||||
* },
|
||||
* fill: 'red'
|
||||
* });
|
||||
*/
|
||||
export declare class Ellipse extends Shape<EllipseConfig> {
|
||||
_getStrokePadding(): number;
|
||||
_sceneFunc(context: Context): void;
|
||||
getWidth(): number;
|
||||
getHeight(): number;
|
||||
setWidth(width: number): void;
|
||||
setHeight(height: number): void;
|
||||
radius: GetSet<Vector2d, this>;
|
||||
radiusX: GetSet<number, this>;
|
||||
radiusY: GetSet<number, this>;
|
||||
}
|
||||
/**
|
||||
* get/set radius y
|
||||
* @name Konva.Ellipse#radiusY
|
||||
* @method
|
||||
* @param {Number} y
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* // get radius y
|
||||
* var radiusY = ellipse.radiusY();
|
||||
*
|
||||
* // set radius y
|
||||
* ellipse.radiusY(200);
|
||||
*/
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Shape } from "../Shape.js";
|
||||
import { getNumberValidator } from "../Validators.js";
|
||||
import { _registerNode } from "../Global.js";
|
||||
/**
|
||||
* Ellipse constructor
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Shape
|
||||
* @param {Object} config
|
||||
* @param {Object} config.radius defines x and y radius
|
||||
* @@shapeParams
|
||||
* @@nodeParams
|
||||
* @example
|
||||
* var ellipse = new Konva.Ellipse({
|
||||
* radius : {
|
||||
* x : 50,
|
||||
* y : 50
|
||||
* },
|
||||
* fill: 'red'
|
||||
* });
|
||||
*/
|
||||
export class Ellipse extends Shape {
|
||||
_getStrokePadding() {
|
||||
return super._getStrokePadding(1);
|
||||
}
|
||||
_sceneFunc(context) {
|
||||
const rx = Math.abs(this.radiusX()), ry = Math.abs(this.radiusY());
|
||||
context.beginPath();
|
||||
context.ellipse(0, 0, rx, ry, 0, 0, Math.PI * 2, false);
|
||||
context.closePath();
|
||||
context.fillStrokeShape(this);
|
||||
}
|
||||
getWidth() {
|
||||
return Math.abs(this.radiusX()) * 2;
|
||||
}
|
||||
getHeight() {
|
||||
return Math.abs(this.radiusY()) * 2;
|
||||
}
|
||||
setWidth(width) {
|
||||
this.radiusX(width / 2);
|
||||
}
|
||||
setHeight(height) {
|
||||
this.radiusY(height / 2);
|
||||
}
|
||||
}
|
||||
Ellipse.prototype.className = 'Ellipse';
|
||||
Ellipse.prototype._centroid = true;
|
||||
Ellipse.prototype._attrsAffectingSize = ['radiusX', 'radiusY'];
|
||||
_registerNode(Ellipse, true);
|
||||
// add getters setters
|
||||
Factory.addComponentsGetterSetter(Ellipse, 'radius', ['x', 'y']);
|
||||
/**
|
||||
* get/set radius
|
||||
* @name Konva.Ellipse#radius
|
||||
* @method
|
||||
* @param {Object} radius
|
||||
* @param {Number} radius.x
|
||||
* @param {Number} radius.y
|
||||
* @returns {Object}
|
||||
* @example
|
||||
* // get radius
|
||||
* var radius = ellipse.radius();
|
||||
*
|
||||
* // set radius
|
||||
* ellipse.radius({
|
||||
* x: 200,
|
||||
* y: 100
|
||||
* });
|
||||
*/
|
||||
Factory.addGetterSetter(Ellipse, 'radiusX', 0, getNumberValidator());
|
||||
/**
|
||||
* get/set radius x
|
||||
* @name Konva.Ellipse#radiusX
|
||||
* @method
|
||||
* @param {Number} x
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* // get radius x
|
||||
* var radiusX = ellipse.radiusX();
|
||||
*
|
||||
* // set radius x
|
||||
* ellipse.radiusX(200);
|
||||
*/
|
||||
Factory.addGetterSetter(Ellipse, 'radiusY', 0, getNumberValidator());
|
||||
/**
|
||||
* get/set radius y
|
||||
* @name Konva.Ellipse#radiusY
|
||||
* @method
|
||||
* @param {Number} y
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* // get radius y
|
||||
* var radiusY = ellipse.radiusY();
|
||||
*
|
||||
* // set radius y
|
||||
* ellipse.radiusY(200);
|
||||
*/
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
import type { ShapeConfig } from '../Shape.ts';
|
||||
import { Shape } from '../Shape.ts';
|
||||
import type { GetSet, IRect } from '../types.ts';
|
||||
import type { Context } from '../Context.ts';
|
||||
export interface ImageConfig extends ShapeConfig {
|
||||
image: CanvasImageSource | undefined;
|
||||
crop?: IRect;
|
||||
cornerRadius?: number | number[];
|
||||
}
|
||||
/**
|
||||
* Image constructor
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Shape
|
||||
* @param {Object} config
|
||||
* @param {Image} config.image
|
||||
* @param {Object} [config.crop]
|
||||
* @@shapeParams
|
||||
* @@nodeParams
|
||||
* @example
|
||||
* var imageObj = new Image();
|
||||
* imageObj.onload = function() {
|
||||
* var image = new Konva.Image({
|
||||
* x: 200,
|
||||
* y: 50,
|
||||
* image: imageObj,
|
||||
* width: 100,
|
||||
* height: 100
|
||||
* });
|
||||
* };
|
||||
* imageObj.src = '/path/to/image.jpg'
|
||||
*/
|
||||
export declare class Image extends Shape<ImageConfig> {
|
||||
private _loadListener;
|
||||
constructor(attrs?: ImageConfig);
|
||||
_setImageLoad(): void;
|
||||
_removeImageLoad(image: any): void;
|
||||
destroy(): this;
|
||||
_useBufferCanvas(forceFill?: boolean, opacity?: any): boolean;
|
||||
_getStrokePadding(): number;
|
||||
_sceneFunc(context: Context): void;
|
||||
_hitFunc(context: Context): void;
|
||||
getWidth(): any;
|
||||
getHeight(): any;
|
||||
/**
|
||||
* load image from given url and create `Konva.Image` instance
|
||||
* @method
|
||||
* @memberof Konva.Image
|
||||
* @param {String} url image source
|
||||
* @param {Function} callback with Konva.Image instance as first argument
|
||||
* @param {Function} onError optional error handler
|
||||
* @example
|
||||
* Konva.Image.fromURL(imageURL, function(image){
|
||||
* // image is Konva.Image instance
|
||||
* layer.add(image);
|
||||
* });
|
||||
*/
|
||||
static fromURL(url: string, callback: (img: Image) => void, onError?: OnErrorEventHandler): void;
|
||||
image: GetSet<CanvasImageSource | undefined, this>;
|
||||
crop: GetSet<IRect, this>;
|
||||
cropX: GetSet<number, this>;
|
||||
cropY: GetSet<number, this>;
|
||||
cropWidth: GetSet<number, this>;
|
||||
cropHeight: GetSet<number, this>;
|
||||
cornerRadius: GetSet<number | number[], this>;
|
||||
}
|
||||
/**
|
||||
* get/set crop height
|
||||
* @name Konva.Image#cropHeight
|
||||
* @method
|
||||
* @param {Number} height
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* // get crop height
|
||||
* var cropHeight = image.cropHeight();
|
||||
*
|
||||
* // set crop height
|
||||
* image.cropHeight(20);
|
||||
*/
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
import { Util } from "../Util.js";
|
||||
import { Factory } from "../Factory.js";
|
||||
import { Shape } from "../Shape.js";
|
||||
import { _registerNode } from "../Global.js";
|
||||
import { getNumberOrArrayOfNumbersValidator, getNumberValidator, } from "../Validators.js";
|
||||
/**
|
||||
* Image constructor
|
||||
* @constructor
|
||||
* @memberof Konva
|
||||
* @augments Konva.Shape
|
||||
* @param {Object} config
|
||||
* @param {Image} config.image
|
||||
* @param {Object} [config.crop]
|
||||
* @@shapeParams
|
||||
* @@nodeParams
|
||||
* @example
|
||||
* var imageObj = new Image();
|
||||
* imageObj.onload = function() {
|
||||
* var image = new Konva.Image({
|
||||
* x: 200,
|
||||
* y: 50,
|
||||
* image: imageObj,
|
||||
* width: 100,
|
||||
* height: 100
|
||||
* });
|
||||
* };
|
||||
* imageObj.src = '/path/to/image.jpg'
|
||||
*/
|
||||
export class Image extends Shape {
|
||||
constructor(attrs) {
|
||||
super(attrs);
|
||||
this._loadListener = () => {
|
||||
this._requestDraw();
|
||||
};
|
||||
this.on('imageChange.konva', (props) => {
|
||||
this._removeImageLoad(props.oldVal);
|
||||
this._setImageLoad();
|
||||
});
|
||||
this._setImageLoad();
|
||||
}
|
||||
_setImageLoad() {
|
||||
var _a;
|
||||
const image = this.image();
|
||||
// A fresh `new Image()` with no src already reports complete === true,
|
||||
// so it still needs the listener.
|
||||
if (((image === null || image === void 0 ? void 0 : image.complete) && image.src) || (image === null || image === void 0 ? void 0 : image.readyState) >= 2) {
|
||||
return;
|
||||
}
|
||||
(_a = image === null || image === void 0 ? void 0 : image.addEventListener) === null || _a === void 0 ? void 0 : _a.call(image, 'videoWidth' in image ? 'loadeddata' : 'load', this._loadListener);
|
||||
}
|
||||
_removeImageLoad(image) {
|
||||
var _a;
|
||||
(_a = image === null || image === void 0 ? void 0 : image.removeEventListener) === null || _a === void 0 ? void 0 : _a.call(image, 'videoWidth' in image ? 'loadeddata' : 'load', this._loadListener);
|
||||
}
|
||||
destroy() {
|
||||
this._removeImageLoad(this.image());
|
||||
super.destroy();
|
||||
return this;
|
||||
}
|
||||
_useBufferCanvas(forceFill, opacity = this.getAbsoluteOpacity()) {
|
||||
if (this.attrs.perfectDrawEnabled === false)
|
||||
return false;
|
||||
const hasCornerRadius = !!this.cornerRadius();
|
||||
const hasShadow = this.hasShadow();
|
||||
if (hasCornerRadius && hasShadow) {
|
||||
return true;
|
||||
}
|
||||
return super._useBufferCanvas(true, opacity);
|
||||
}
|
||||
_getStrokePadding() {
|
||||
return super._getStrokePadding(this.strokeScaleEnabled() ? 1 : undefined);
|
||||
}
|
||||
_sceneFunc(context) {
|
||||
const width = this.getWidth();
|
||||
const height = this.getHeight();
|
||||
const cornerRadius = this.cornerRadius();
|
||||
const image = this.attrs.image;
|
||||
if (this.hasFill() || this.hasStroke() || cornerRadius) {
|
||||
context.beginPath();
|
||||
cornerRadius
|
||||
? Util.drawRoundedRectPath(context, width, height, cornerRadius)
|
||||
: context.rect(0, 0, width, height);
|
||||
context.closePath();
|
||||
context.fillStrokeShape(this);
|
||||
}
|
||||
if (image) {
|
||||
if (cornerRadius) {
|
||||
context.clip();
|
||||
}
|
||||
const cropWidth = this.attrs.cropWidth;
|
||||
const cropHeight = this.attrs.cropHeight;
|
||||
if (cropWidth && cropHeight) {
|
||||
context.drawImage(image, this.cropX(), this.cropY(), cropWidth, cropHeight, 0, 0, width, height);
|
||||
}
|
||||
else {
|
||||
context.drawImage(image, 0, 0, width, height);
|
||||
}
|
||||
}
|
||||
// If you need to draw later, you need to execute save/restore
|
||||
}
|
||||
_hitFunc(context) {
|
||||
const width = this.width(), height = this.height(), cornerRadius = this.cornerRadius();
|
||||
context.beginPath();
|
||||
if (!cornerRadius) {
|
||||
context.rect(0, 0, width, height);
|
||||
}
|
||||
else {
|
||||
Util.drawRoundedRectPath(context, width, height, cornerRadius);
|
||||
}
|
||||
context.closePath();
|
||||
context.fillStrokeShape(this);
|
||||
}
|
||||
getWidth() {
|
||||
var _a, _b, _c;
|
||||
const image = this.image();
|
||||
return (_c = (_b = (_a = this.attrs.width) !== null && _a !== void 0 ? _a : image === null || image === void 0 ? void 0 : image.videoWidth) !== null && _b !== void 0 ? _b : image === null || image === void 0 ? void 0 : image.width) !== null && _c !== void 0 ? _c : 0;
|
||||
}
|
||||
getHeight() {
|
||||
var _a, _b, _c;
|
||||
const image = this.image();
|
||||
return (_c = (_b = (_a = this.attrs.height) !== null && _a !== void 0 ? _a : image === null || image === void 0 ? void 0 : image.videoHeight) !== null && _b !== void 0 ? _b : image === null || image === void 0 ? void 0 : image.height) !== null && _c !== void 0 ? _c : 0;
|
||||
}
|
||||
/**
|
||||
* load image from given url and create `Konva.Image` instance
|
||||
* @method
|
||||
* @memberof Konva.Image
|
||||
* @param {String} url image source
|
||||
* @param {Function} callback with Konva.Image instance as first argument
|
||||
* @param {Function} onError optional error handler
|
||||
* @example
|
||||
* Konva.Image.fromURL(imageURL, function(image){
|
||||
* // image is Konva.Image instance
|
||||
* layer.add(image);
|
||||
* });
|
||||
*/
|
||||
static fromURL(url, callback, onError = null) {
|
||||
const img = Util.createImageElement();
|
||||
img.onload = () => {
|
||||
img.onload = img.onerror = null;
|
||||
callback(new Image({ image: img }));
|
||||
};
|
||||
img.onerror = onError;
|
||||
img.crossOrigin = 'Anonymous';
|
||||
img.src = url;
|
||||
}
|
||||
}
|
||||
Image.prototype.className = 'Image';
|
||||
Image.prototype._attrsAffectingSize = ['image'];
|
||||
_registerNode(Image, true);
|
||||
/**
|
||||
* get/set corner radius
|
||||
* @method
|
||||
* @name Konva.Image#cornerRadius
|
||||
* @param {Number} cornerRadius
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* // get corner radius
|
||||
* var cornerRadius = image.cornerRadius();
|
||||
*
|
||||
* // set corner radius
|
||||
* image.cornerRadius(10);
|
||||
*
|
||||
* // set different corner radius values
|
||||
* // top-left, top-right, bottom-right, bottom-left
|
||||
* image.cornerRadius([0, 10, 20, 30]);
|
||||
*/
|
||||
Factory.addGetterSetter(Image, 'cornerRadius', 0, getNumberOrArrayOfNumbersValidator(4));
|
||||
/**
|
||||
* get/set image source. It can be image, canvas or video element
|
||||
* @name Konva.Image#image
|
||||
* @method
|
||||
* @param {Object} image source
|
||||
* @returns {Object}
|
||||
* @example
|
||||
* // get value
|
||||
* var image = shape.image();
|
||||
*
|
||||
* // set value
|
||||
* shape.image(img);
|
||||
*/
|
||||
Factory.addGetterSetter(Image, 'image');
|
||||
Factory.addComponentsGetterSetter(Image, 'crop', ['x', 'y', 'width', 'height']);
|
||||
/**
|
||||
* get/set crop
|
||||
* @method
|
||||
* @name Konva.Image#crop
|
||||
* @param {Object} crop
|
||||
* @param {Number} crop.x
|
||||
* @param {Number} crop.y
|
||||
* @param {Number} crop.width
|
||||
* @param {Number} crop.height
|
||||
* @returns {Object}
|
||||
* @example
|
||||
* // get crop
|
||||
* var crop = image.crop();
|
||||
*
|
||||
* // set crop
|
||||
* image.crop({
|
||||
* x: 20,
|
||||
* y: 20,
|
||||
* width: 20,
|
||||
* height: 20
|
||||
* });
|
||||
*/
|
||||
Factory.addGetterSetter(Image, 'cropX', 0, getNumberValidator());
|
||||
/**
|
||||
* get/set crop x
|
||||
* @method
|
||||
* @name Konva.Image#cropX
|
||||
* @param {Number} x
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* // get crop x
|
||||
* var cropX = image.cropX();
|
||||
*
|
||||
* // set crop x
|
||||
* image.cropX(20);
|
||||
*/
|
||||
Factory.addGetterSetter(Image, 'cropY', 0, getNumberValidator());
|
||||
/**
|
||||
* get/set crop y
|
||||
* @name Konva.Image#cropY
|
||||
* @method
|
||||
* @param {Number} y
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* // get crop y
|
||||
* var cropY = image.cropY();
|
||||
*
|
||||
* // set crop y
|
||||
* image.cropY(20);
|
||||
*/
|
||||
Factory.addGetterSetter(Image, 'cropWidth', 0, getNumberValidator());
|
||||
/**
|
||||
* get/set crop width
|
||||
* @name Konva.Image#cropWidth
|
||||
* @method
|
||||
* @param {Number} width
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* // get crop width
|
||||
* var cropWidth = image.cropWidth();
|
||||
*
|
||||
* // set crop width
|
||||
* image.cropWidth(20);
|
||||
*/
|
||||
Factory.addGetterSetter(Image, 'cropHeight', 0, getNumberValidator());
|
||||
/**
|
||||
* get/set crop height
|
||||
* @name Konva.Image#cropHeight
|
||||
* @method
|
||||
* @param {Number} height
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* // get crop height
|
||||
* var cropHeight = image.cropHeight();
|
||||
*
|
||||
* // set crop height
|
||||
* image.cropHeight(20);
|
||||
*/
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user