🚀 Big News:Socket Has Acquired Secure Annex.Learn More
Socket
Book a DemoSign in
Socket

konva

Package Overview
Dependencies
Maintainers
1
Versions
240
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

konva - npm Package Compare versions

Comparing version
0.12.4
to
0.13.0
+830
konva.d.ts
declare module Konva {
var pixelRatio : number;
var dragDistance: number;
var isDragging : () => boolean;
var isDragReady : () => boolean;
var DD : any;
export class Util {
static getRandomColor() : string;
static getRGB(color: string) : string;
}
export class Easings {
static BackEaseIn() : any;
static BackEaseInOut() : any;
static BackEaseOut() : any;
static BounceEaseIn() : any;
static BounceEaseInOut() : any;
static BounceEaseOut() : any;
static EaseIn() : any;
static EaseInOut() : any;
static EaseOut() : any;
static ElasticEaseIn() : any;
static ElasticEaseInOut() : any;
static ElasticEaseOut() : any;
static Linear() : any;
static StrongEaseIn() : any;
static StrongEaseInOut() : any;
static StrongEaseOut() : any;
}
class Filter {
}
export class Filters {
static Blur(imageData : any): Filter;
static Brighten(imageData : any): Filter;
static Emboss(imageData : any): Filter;
static Enhance(imageData : any): Filter;
static Grayscale(imageData : any): Filter;
static HSV(imageData : any): Filter;
static Invert(imageData : any): Filter;
static Mask(imageData : any): Filter;
static Noise(imageData : any): Filter;
static Pixelate(imageData : any): Filter;
static Posterize(imageData : any): Filter;
static RGB(imageData : any): Filter;
static RGA(imageData : any): Filter;
static Sepia(imageData : any): Filter;
static Solarize(imageData : any): Filter;
static Threshold(imageData : any): Filter;
}
export class Animation {
constructor(func: Function, layers?: Konva.Layer[]);
constructor(func: Function, layer?: Konva.Layer);
addLayer(layer: Konva.Layer) : boolean;
getLayers() : Konva.Layer[];
isRunning() : boolean;
setLayers(layers : Konva.Layer[]) : Animation;
setLayers(layer : Konva.Layer) : Animation;
start() : Animation;
stop() : Animation;
}
interface NodeConfig {
x?: number;
y?: number;
width? : number;
height? : number;
visible?: boolean;
listening?: boolean;
id?: string;
name?: string;
opacity?: Number;
scale?: Vector2d;
scaleX? : number;
scaleY? : number;
rotation?: number;
rotationDeg?: number;
offset?: Vector2d;
offsetX? : number;
offsetY? : number;
draggable?: boolean;
dragBoundFunc?: Function;
}
interface SizeConfig {
x? : number;
y? : number;
width? : number;
height? : number;
}
interface ToDataURLConfig extends SizeConfig {
callback : Function;
mimeType? : string;
quality? : number;
}
interface CacheConfig extends SizeConfig{
drawBorder? : boolean;
}
interface ClearConfig extends SizeConfig{
}
class Node {
constructor (config: NodeConfig);
static create<T>(data: any, container?: HTMLElement) : T;
blue() : number;
blue(blue: number) : Node;
brightness() : number;
brightness(brightness: number) : Node;
blurRadius() : number;
blurRadius(radius: number) : Node;
cache(config?: CacheConfig) : Node;
clearCache() : Node;
clear(bounds?: ClearConfig) : Node;
clone(attrs? : NodeConfig): Node;
destroy() : void;
dragBoundFunc() : Function;
dragBoundFunc(dragBoundFunc: Function) : Node;
draggable() : boolean;
draggable(draggable: boolean) : Node;
draw() : Node;
embossBlend() : boolean;
embossBlend(embossBlend: boolean) : Node;
embossDirection() : string;
embossDirection(embossDirection: string) : Node;
embossStrength() : number;
embossStrength(level: number) : Node;
embossWhiteLevel() : number;
embossWhiteLevel(embossWhiteLevel: number) : Node;
enhance() : number;
enhance(enhance: number) : Node;
filters() : Filter[];
filters(filters : Filter) : Node;
fire(eventType: string, evt?: any, bubble?: boolean) : Node;
getAbsoluteOpacity(): number;
getAbsolutePosition(): Vector2d;
getAbsoluteTransform(): Transform;
getAbsoluteZIndex(): number;
getAncestors() : Collection;
getAttr(attr: string): any;
getAttrs(): NodeConfig;
// CHECK
getCanvas() : Canvas;
getClassName() : string;
getClientRect(): SizeConfig;
getContext() : Context;
getDepth() : number;
getHeight() : number;
getHitCanvas() : Canvas;
getLayer() : Layer;
getParent() : Container;
// CHECK
getSize() : {
width : number;
height : number;
};
getStage() : Stage;
getTransform() : Transform;
getType() : String;
getWidth() : number;
getZIndex(): number;
green() : number;
green(green: number) : Node;
height() : number;
height(height: number) : Node;
hide(): void;
hue() : number;
hue(hue: number) : Node;
id() : string;
id(id: string) : Node;
isDragging(): boolean;
isListening(): boolean;
isVisible(): boolean;
kaleidoscopeAngle() : number;
kaleidoscopeAngle(kaleidoscopeAngle: number) : Node;
kaleidoscopePower() : number;
kaleidoscopePower(kaleidoscopePower: number) : Node;
levels() : number;
levels(levels: number) : Node;
listening() : any;
listening(listening: boolean) : Node;
listening(listening : string) : Node;
move(move : Vector2d) : Node;
moveDown() : boolean;
moveTo(newContainer: Container): Node;
moveToBottom(): boolean;
moveToTop(): boolean;
moveUp(): boolean;
name() : string;
name(name: string) : Node;
noise() : number;
noise(noise: number) : Node;
off(evtStr : string) : Node;
offset() : Vector2d;
offset(offset: Vector2d) : Node;
offsetX() : number;
offsetX(offsetX: number) : Node;
offsetY() : number;
offsetY(offsetY: number) : Node;
on(evtStr : string, handler: Function) : Node;
opacity() : number;
opacity(opacity: number) : Node;
pixelSize() : number;
pixelSize(pixelSize: number) : Node;
position() : Vector2d;
position(position: Vector2d) : Node;
red() : number;
red(red: number) : Node;
remove() : Node;
rotate(theta : number) : Node;
rotation() : number;
rotation(rotation: number) : Node;
saturation() : number;
saturation(saturation: number) : Node;
scale() : Vector2d;
scale(scale: Vector2d) : Node;
scaleX() : number;
scaleX(scaleX: number) : Node;
scaleY() : number;
scaleY(scaleY: number) : Node;
setAbsolutePosition(pos : Vector2d) : Node;
setAttr(attr: string, val : any): Node;
setAttrs(attrs: NodeConfig) : void;
setId(id: string) : Node;
setSize(size: {width:number; height: number}) : Node;
setZIndex(zIndex: number): void;
shouldDrawHit() : boolean;
show() : Node;
skew() : Vector2d;
skew(skew: Vector2d) : Node;
skewX() : number;
skewX(skewX: number) : Node;
skewY() : number;
skewY(skewY: number) : Node;
startDrag() : void;
stopDrag() : void;
threshold() : number;
threshold(threshold: number) : Node;
toDataURL(config: ToDataURLConfig) : string;
toImage(config: ToDataURLConfig) : HTMLImageElement;
toJSON() : string;
toObject() : any;
transformsEnabled() : string;
transformsEnabled(transformsEnabled: string) : Node;
value() : number;
value(value: number) : Node;
visible() : any;
visible(visible: boolean) : Node;
visible(visible: string) : Node;
width() : number;
width(width: number) : Node;
x() : number;
x(x: number) : Node;
y() : number;
y(y: number) : Node;
}
interface ContainerConfig extends NodeConfig {
clearBeforeDraw?: boolean;
clipFunc?: Function;
}
class Container extends Node {
constructor(params?: ContainerConfig);
add(child : Node): Container;
getChildren(filterfunc?: Function) : Collection;
clip(): SizeConfig;
clip(clip: SizeConfig) : Container;
clipHeight(): number;
clipHeight(clipHeight: number) : Container;
clipWidth(): number;
clipWidth(clipWidth: number) : Container;
clipX(): number;
clipX(clipX: number) : Container;
clipY(): number;
clipY(clipY: number) : Container;
destroyChildren() : void;
find(selector? : string): Collection;
getAllIntersections(pos: Vector2d): Node[];
hasChildren() : boolean;
removeChildren() : void;
}
interface ShapeConfig extends NodeConfig {
fill?: string;
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?: string[];
fillLinearRadialStartPoint?: Vector2d;
fillLinearRadialStartPointX?: number;
fillLinearRadialStartPointY?: number;
fillLinearRadialEndPoint? : Vector2d;
fillLinearRadialEndPointX?: number;
fillLinearRadialEndPointY?: number;
fillRadialGradientStartRadius?: number;
fillRadialGradientEndRadius?: number;
fillRadialGradientColorStops?: string[];
fillEnabled?: boolean;
fillPriority?: string;
stroke?: string;
strokeWidth?: number;
strokeScaleEnabled?: boolean;
strokeEnabled?: boolean;
lineJoin?: string;
lineCap?: string;
sceneFunc? : (con : Context) => void;
hitFunc? : (con : Context) => void;
drawFunc? : (con : Context) => void;
shadowColor?: string;
shadowBlur?: number;
shadowOffset? : Vector2d;
shadowOffsetX? : number;
shadowOffsetY? : number;
shadowOpacity?: number;
shadowEnabled?: boolean;
dash?: number[];
dashEnabled?: boolean;
}
class Shape extends Node {
constructor(ShapeConfig : ShapeConfig);
dash() : number[];
dash(dash: number[]): Shape;
dashEnabled() : boolean;
dashEnabled(dashEnabled: boolean): Shape;
drawHitFromCache(alphaThreshold: number): Shape;
fill() : string;
fill(fill: string): Shape;
fillEnabled() : boolean;
fillEnabled(fillEnabled: boolean): Shape;
fillLinearGradientColorStops() : string[];
fillLinearGradientColorStops(colors: string[]): Shape;
fillLinearGradientStartPoint(): Vector2d;
fillLinearGradientStartPoint(point: Vector2d): Vector2d;
fillLinearGradientStartPointX(): number;
fillLinearGradientStartPointX(x: number): Shape;
fillLinearGradientStartPointY(): number;
fillLinearGradientStartPointY(y: number): Shape;
fillLinearGradientEndPoint() : Vector2d;
fillLinearGradientEndPoint(point: Vector2d) : Shape;
fillLinearGradientEndPointX(): number;
fillLinearGradientEndPointX(x: number): Shape;
fillLinearGradientEndPointY(): number;
fillLinearGradientEndPointY(y: number): Shape;
fillLinearRadialStartPoint(): Vector2d;
fillLinearRadialStartPoint(point: Vector2d): Shape;
fillLinearRadialStartPointX(): number;
fillLinearRadialStartPointX(x: number): Shape;
fillLinearRadialStartPointY(): number;
fillLinearRadialStartPointY(y: number): Shape;
fillLinearRadialEndPoint() : Vector2d;
fillLinearRadialEndPoint(point: Vector2d) : Vector2d;
fillLinearRadialEndPointX(): number;
fillLinearRadialEndPointX(x: number): Shape;
fillLinearRadialEndPointY(): number;
fillLinearRadialEndPointY(y: number): Shape;
fillPatternImage(): HTMLImageElement;
fillPatternImage(image: HTMLImageElement): Shape;
fillRadialGradientStartRadius(): number;
fillRadialGradientStartRadius(radius: number): Shape;
fillRadialGradientEndRadius(): number;
fillRadialGradientEndRadius(radius: number): Shape;
fillRadialGradientColorStops(): string[];
fillRadialGradientColorStops(color: string[]): Shape;
fillPatternOffset(): Vector2d;
fillPatternOffset(offset: Vector2d) : Shape;
fillPatternOffsetX(): number;
fillPatternOffsetX(x: number): Shape;
fillPatternOffsetY(): number;
fillPatternOffsetY(y: number): Shape;
fillPatternRepeat() : string;
fillPatternRepeat(repeat: string): Shape;
fillPatternRotation(): number;
fillPatternRotation(rotation: number): Shape;
fillPatternScale(): Vector2d;
fillPatternScale(scale: Vector2d) : Shape;
fillPatternScaleX(): number;
fillPatternScaleX(x: number): Shape;
fillPatternScaleY(): number;
fillPatternScaleY(y: number): Shape;
fillPatternX(): number;
fillPatternX(x: number): number;
fillPatternY(): number;
fillPatternY(y: number): Shape;
fillPriority(): string;
fillPriority(priority: string): Shape;
hasFill(): boolean;
hasShadow(): boolean;
hasStroke(): boolean;
hitFunc(): Function;
hitFunc(func: Function): Shape;
intersects(point: Vector2d): boolean;
lineCap() : string;
lineCap(lineCap: string): Shape;
lineJoin() : string;
lineJoin(lineJoin: string): Shape;
sceneFunc(): Function;
sceneFunc(func: (con : Context) => {}): Shape;
shadowColor() : string;
shadowColor(shadowColor: string): Shape;
shadowEnabled() : boolean;
shadowEnabled(shadowEnabled: boolean): Shape;
shadowOffset() : Vector2d;
shadowOffset(shadowOffset: Vector2d): Shape;
shadowOffsetX() : number;
shadowOffsetX(shadowOffsetX: number): Shape;
shadowOffsetY() : number;
shadowOffsetY(shadowOffsetY: number): Shape;
shadowOpacity() : number;
shadowOpacity(shadowOpacity: number): Shape;
shadowBlur() : number;
shadowBlur(blur: number): Shape;
stroke() : string;
stroke(stroke: string): Shape;
strokeEnabled() : boolean;
strokeEnabled(strokeEnabled: boolean): Shape;
strokeScaleEnabled() : boolean;
strokeScaleEnabled(strokeScaleEnabled: boolean): Shape;
strokeHitEnabled() : boolean;
strokeHitEnabled(strokeHitEnabled: boolean): Shape;
strokeWidth() : number;
strokeWidth(strokeWidth: number): Shape;
}
interface StageConfig extends ContainerConfig {
container: any;
}
class Stage extends Container {
constructor(StageConfig : StageConfig);
add(layer: Layer): Stage;
batchDraw(): void;
container(): HTMLElement;
destroy() : void;
drawHit(): void;
getIntersection(pos: Vector2d) : Shape;
getLayers(): Layer[];
getPointerPosition(): Vector2d;
setContainer(con: HTMLElement): void;
setHeight(height: number) : void;
setWidth(width: number) : void;
}
interface LayerConfig extends ContainerConfig {
clearBeforeDraw?: boolean;
}
class Layer extends Container {
constructor (config?: LayerConfig);
getIntersection(pos: Vector2d): Shape;
enableHitGraph(): Layer;
disableHitGraph(): Layer;
clearBeforeDraw() : boolean;
clearBeforeDraw(val: boolean): Layer;
hitGraphEnabled(): boolean;
hitGraphEnabled(val: boolean): Layer;
batchDraw(): void;
drawScene() : void;
}
class Group extends Container {
}
interface CanvasConfig {
width: number;
height: number;
pixelRatio: number;
}
class Canvas {
constructor(CanvasConfig : CanvasConfig);
getContext(): CanvasRenderingContext2D;
getHeight(): number;
getWidth(): number;
getPixelRatio(): number;
setHeight(val: number) : void;
setWidth(val: number) : void ;
setPixelRatio(val: number) : void;
setSize(size: {width:number; height: number}) : void;
toDataURL(mimeType: string, quality: number) : string;
public _canvas : HTMLElement;
}
class Context {
clear(bounds?: ClearConfig) : Context;
clearTrace(): void;
fillShape(shape: Shape): void;
fillStrokeShape(shape: Shape): void;
getCanvas() : Konva.Canvas;
getTrace(relaxed: boolean): string;
reset(): void;
moveTo(x : number, y : number) : void;
lineTo(x : number, y : number) : void;
beginPath() : void;
setAttr(attr : string, value : any) : void;
closePath() : void;
strokeShape(shape: Shape): void;
}
class Tween {
constructor(params: any);
destroy(): void;
finish(): Tween;
pause(): Tween;
play(): Tween;
reset(): Tween;
reverse(): Tween;
seek(t: number): Tween;
}
// Shapes
interface RingConfig extends ShapeConfig {
innerRadius: number;
outerRadius: number;
clockwise?: boolean;
}
class Ring extends Shape {
constructor(RingConfig : RingConfig);
angle(): number;
angle(angle: number): Ring;
innerRadius(): number;
innerRadius(innerRadius: number): Ring;
outerRadius(): number;
outerRadius(outerRadius: number): Ring;
}
interface ArcConfig extends RingConfig {
angle: number;
}
class Arc extends Shape {
constructor(ArcConfig : ArcConfig);
clockwise(): boolean;
clockwise(clockwise: boolean): Arc;
}
interface CircleConfig extends ShapeConfig {
radius: number;
}
class Circle extends Shape {
constructor(CircleConfig : CircleConfig);
radius(): number;
radius(radius: number): Circle;
}
interface EllipseConfig extends ShapeConfig {
radius: any;
}
class Ellipse extends Shape {
constructor(EllipseConfig : EllipseConfig);
radius(): any;
radius(radius: any): Ellipse;
radiusX(): number;
radiusX(radiusX: number): Ellipse;
radiusY(): number;
radiusY(radiusY: number): Ellipse;
}
interface ImageConfig extends ShapeConfig {
image: HTMLImageElement;
crop?: SizeConfig;
}
class Image extends Shape {
constructor(ImageConfig : ImageConfig);
image(): HTMLImageElement;
image(image: HTMLImageElement): Image;
crop(): SizeConfig;
crop(crop: SizeConfig): Image;
cropX(): number;
cropX(cropX: number): Image;
cropY(): number;
cropY(cropY: number): Image;
cropWidth(): number;
cropWidth(cropWidth: number): Image;
cropHeight(): number;
cropHeight(cropHeight: number): Image;
}
interface LineConfig extends ShapeConfig {
points: number[];
tension?: number;
closed?: boolean;
}
class Line extends Shape {
constructor(LineConfig : LineConfig);
closed(): boolean;
closed(closed: boolean): Line;
tension(): number;
tension(tension: number): Line;
points(): number[];
points(points: number[]): Line;
}
interface RectConfig extends ShapeConfig {
cornerRadius?: number;
}
class Rect extends Shape {
constructor(RectConfig : RectConfig);
cornerRadius(): number;
cornerRadius(cornerRadius: number): Rect;
}
interface SpriteConfig extends ShapeConfig {
animation: string;
animations: any;
frameIndex?: number;
image: HTMLImageElement;
}
class Sprite extends Shape {
constructor(SpriteConfig : SpriteConfig);
start(): void;
stop(): void;
animation(): string;
animation(val: string): Sprite;
animations(): any;
animations(val: any): Sprite;
frameIndex(): number;
frameIndex(val: number): Sprite;
image(): HTMLImageElement;
image(image: HTMLImageElement): Sprite;
frameRate(): number;
frameRate(frameRate: number): Sprite;
}
interface TextConfig extends ShapeConfig {
text: string;
fontFamily?: string;
fontSize?: number;
fontStyle?: string;
align?: string;
padding?: number;
lineHeight?: number;
wrap?: string;
}
class Text extends Shape {
constructor(TextConfig : TextConfig);
getTextWidth(): number;
getTextHeight(): number;
text(): string;
text(text: string): Text;
fontFamily(): string;
fontFamily(fontFamily: string): Text;
fontSize(): number;
fontSize(fontSize: number): Text;
fontStyle(): string;
fontStyle(fontStyle: string): Text;
fontVariant(): string;
fontVariant(fontVariant: string): Text;
align(): string;
align(align: string): Text;
padding(): number;
padding(padding: number): Text;
lineHeight(): number;
lineHeight(lineHeight: number): Text;
wrap(): string;
wrap(wrap: string): Text;
}
interface WedgeConfig extends ShapeConfig {
angle: number;
radius: number;
clockwise?: boolean;
}
class Wedge extends Shape {
constructor(WedgeConfig : WedgeConfig);
angle(): number;
angle(angle: number): Wedge;
radius(): number;
radius(radius: number): Wedge;
clockwise(): boolean;
clockwise(clockwise: boolean): Wedge;
}
// Plugins
interface TagConfig extends ShapeConfig {
pointerDirection?: string;
pointerWidth?: number;
pointerHeight?: number;
cornerRadius?:number;
}
class Tag extends Shape {
constructor(config : TagConfig);
pointerDirection(): string;
pointerDirection(pointerDirection: string): Tag;
pointerWidth(): number;
pointerWidth(pointerWidth: number): Tag;
pointerHeight(): number;
pointerHeight(pointerHeight: number): Tag;
cornerRadius(): number;
cornerRadius(cornerRadius: number): Tag;
}
interface LabelInterface extends ContainerConfig {
}
class Label extends Group {
constructor(LabelInterface : LabelInterface);
getText(): Text;
getTag(): Rect;
}
interface PathConfig extends ShapeConfig {
data: string;
}
class Path extends Shape {
constructor(PathConfig : PathConfig);
data(): string;
data(data: string): Path;
}
interface RegularPolygonConfig extends ShapeConfig {
sides: number;
radius: number;
}
class RegularPolygon extends Shape {
constructor(RegularPolygonConfig : RegularPolygonConfig);
sides(): number;
sides(sides: number): RegularPolygon;
radius(): number;
radius(radius: number): RegularPolygon;
}
interface StarConfig extends ShapeConfig {
numPoints: number;
innerRadius: number;
outerRadius: number;
}
class Star extends Shape {
constructor(StarConfig : StarConfig);
numPoints(): number;
numPoints(numPoints: number): Star;
innerRadius(): number;
innerRadius(innerRadius: number): Star;
outerRadius(): number;
outerRadius(outerRadius: number): Star;
}
interface TextPathConfig extends ShapeConfig {
text: string;
data: string;
fontFamily?: string;
fontSize?: number;
fontStyle?: string;
}
class TextPath extends Shape {
constructor(TextPathConfig : TextPathConfig);
getTextWidth(): number;
getTextHeight(): number;
setText(text: string): void;
text(): string;
text(text: string): Path;
fontFamily(): string;
fontFamily(fontFamily: string): Path;
fontSize(): number;
fontSize(fontSize: number): Path;
fontStyle(): string;
fontStyle(fontStyle: string): Path;
}
class Collection {
[i : number] : any;
static toCollection(arr: any[]): Collection;
each(f: (el : Konva.Node) => void): void;
toArray() : any[];
length: number;
}
class Transform {
getMatrix(): any[];
getTranslation() : Vector2d;
invert() : void;
multiply(matrix: any[]) : void;
rotate(deg: number) : void;
scale(x: number, y: Number) : void;
setAbsolutePosition() : void;
skew(x: number, y: Number) : void;
translate(x: number, y: Number) : void;
}
interface Vector2d {
x: number;
y: number;
}
}
export default Konva;
(function() {
'use strict';
/**
* Arrow constructor
* @constructor
* @memberof Konva
* @augments Konva.Shape
* @param {Object} config
* @param {Array} config.points
* @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
* @param {Number} config.pointerWidth
* @@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
* });
*/
Konva.Arrow = function(config) {
this.____init(config);
};
Konva.Arrow.prototype = {
____init: function(config) {
// call super constructor
Konva.Line.call(this, config);
this.className = 'Arrow';
},
_sceneFunc: function(ctx) {
Konva.Line.prototype._sceneFunc.apply(this, arguments);
var PI2 = Math.PI * 2;
var points = this.points();
var n = points.length;
var dx = points[n - 2] - points[n - 4];
var dy = points[n - 1] - points[n - 3];
var radians = (Math.atan2(dy, dx) + PI2) % PI2;
var length = this.pointerLength();
var width = this.pointerWidth();
ctx.save();
ctx.beginPath();
ctx.translate(points[n - 2], points[n - 1]);
ctx.rotate(radians);
ctx.moveTo(0, 0);
ctx.lineTo(-length, width / 2);
ctx.lineTo(-length, -width / 2);
ctx.closePath();
ctx.restore();
if (this.pointerAtBeginning()) {
ctx.save();
ctx.translate(points[0], points[1]);
dx = points[2] - points[0];
dy = points[3] - points[1];
ctx.rotate((Math.atan2(-dy, -dx) + PI2) % PI2);
ctx.moveTo(0, 0);
ctx.lineTo(-length, width / 2);
ctx.lineTo(-length, -width / 2);
ctx.closePath();
ctx.restore();
}
ctx.fillStrokeShape(this);
}
};
Konva.Util.extend(Konva.Arrow, Konva.Line);
/**
* get/set pointerLength
* @name pointerLength
* @method
* @memberof Konva.Arrow.prototype
* @param {Number} Length of pointer of arrow.
* The default is 10.
* @returns {Number}
* @example
* // get tension
* var pointerLength = line.pointerLength();
*
* // set tension
* line.pointerLength(15);
*/
Konva.Factory.addGetterSetter(Konva.Arrow, 'pointerLength', 10);
/**
* get/set pointerWidth
* @name pointerWidth
* @method
* @memberof Konva.Arrow.prototype
* @param {Number} Width of pointer of arrow.
* The default is 10.
* @returns {Number}
* @example
* // get tension
* var pointerWidth = line.pointerWidth();
*
* // set tension
* line.pointerWidth(15);
*/
Konva.Factory.addGetterSetter(Konva.Arrow, 'pointerWidth', 10);
/**
* get/set pointerAtBeginning
* @name pointerAtBeginning
* @method
* @memberof Konva.Arrow.prototype
* @param {Number} Should pointer displayed at beginning of arrow.
* The default is false.
* @returns {Boolean}
* @example
* // get tension
* var pointerAtBeginning = line.pointerAtBeginning();
*
* // set tension
* line.pointerAtBeginning(true);
*/
Konva.Factory.addGetterSetter(Konva.Arrow, 'pointerAtBeginning', false);
Konva.Collection.mapMethods(Konva.Arrow);
})();
(function() {
'use strict';
// constants
var ATTR_CHANGE_LIST = ['fontFamily', 'fontSize', 'fontStyle', 'padding', 'lineHeight', 'text'],
CHANGE_KONVA = 'Change.konva',
NONE = 'none',
UP = 'up',
RIGHT = 'right',
DOWN = 'down',
LEFT = 'left',
LABEL = 'Label',
// cached variables
attrChangeListLen = ATTR_CHANGE_LIST.length;
/**
* Label constructor.&nbsp; Labels are groups that contain a Text and Tag shape
* @constructor
* @memberof Konva
* @param {Object} config
* @@nodeParams
* @example
* // create label
* var label = new Konva.Label({
* x: 100,
* y: 100,
* draggable: true
* });
*
* // add a tag to the label
* label.add(new Konva.Tag({
* fill: '#bbb',
* stroke: '#333',
* shadowColor: 'black',
* shadowBlur: 10,
* shadowOffset: [10, 10],
* shadowOpacity: 0.2,
* lineJoin: 'round',
* pointerDirection: 'up',
* pointerWidth: 20,
* pointerHeight: 20,
* cornerRadius: 5
* }));
*
* // add text to the label
* label.add(new Konva.Text({
* text: 'Hello World!',
* fontSize: 50,
* lineHeight: 1.2,
* padding: 10,
* fill: 'green'
* }));
*/
Konva.Label = function(config) {
this.____init(config);
};
Konva.Label.prototype = {
____init: function(config) {
var that = this;
Konva.Group.call(this, config);
this.className = LABEL;
this.on('add.konva', function(evt) {
that._addListeners(evt.child);
that._sync();
});
},
/**
* get Text shape for the label. You need to access the Text shape in order to update
* the text properties
* @name getText
* @method
* @memberof Konva.Label.prototype
*/
getText: function() {
return this.find('Text')[0];
},
/**
* get Tag shape for the label. You need to access the Tag shape in order to update
* the pointer properties and the corner radius
* @name getTag
* @method
* @memberof Konva.Label.prototype
*/
getTag: function() {
return this.find('Tag')[0];
},
_addListeners: function(text) {
var that = this,
n;
var func = function(){
that._sync();
};
// update text data for certain attr changes
for(n = 0; n < attrChangeListLen; n++) {
text.on(ATTR_CHANGE_LIST[n] + CHANGE_KONVA, func);
}
},
getWidth: function() {
return this.getText().getWidth();
},
getHeight: function() {
return this.getText().getHeight();
},
_sync: function() {
var text = this.getText(),
tag = this.getTag(),
width, height, pointerDirection, pointerWidth, x, y, pointerHeight;
if (text && tag) {
width = text.getWidth();
height = text.getHeight();
pointerDirection = tag.getPointerDirection();
pointerWidth = tag.getPointerWidth();
pointerHeight = tag.getPointerHeight();
x = 0;
y = 0;
switch(pointerDirection) {
case UP:
x = width / 2;
y = -1 * pointerHeight;
break;
case RIGHT:
x = width + pointerWidth;
y = height / 2;
break;
case DOWN:
x = width / 2;
y = height + pointerHeight;
break;
case LEFT:
x = -1 * pointerWidth;
y = height / 2;
break;
}
tag.setAttrs({
x: -1 * x,
y: -1 * y,
width: width,
height: height
});
text.setAttrs({
x: -1 * x,
y: -1 * y
});
}
}
};
Konva.Util.extend(Konva.Label, Konva.Group);
Konva.Collection.mapMethods(Konva.Label);
/**
* Tag constructor.&nbsp; A Tag can be configured
* to have a pointer element that points up, right, down, or left
* @constructor
* @memberof Konva
* @param {Object} config
* @param {String} [config.pointerDirection] can be up, right, down, left, or none; the default
* is none. When a pointer is present, the positioning of the label is relative to the tip of the pointer.
* @param {Number} [config.pointerWidth]
* @param {Number} [config.pointerHeight]
* @param {Number} [config.cornerRadius]
*/
Konva.Tag = function(config) {
this.___init(config);
};
Konva.Tag.prototype = {
___init: function(config) {
Konva.Shape.call(this, config);
this.className = 'Tag';
this.sceneFunc(this._sceneFunc);
},
_sceneFunc: function(context) {
var width = this.getWidth(),
height = this.getHeight(),
pointerDirection = this.getPointerDirection(),
pointerWidth = this.getPointerWidth(),
pointerHeight = this.getPointerHeight(),
cornerRadius = Math.min(this.getCornerRadius(), width / 2, height / 2);
context.beginPath();
if (!cornerRadius) {
context.moveTo(0, 0);
} else {
context.moveTo(cornerRadius, 0);
}
if (pointerDirection === UP) {
context.lineTo((width - pointerWidth) / 2, 0);
context.lineTo(width / 2, -1 * pointerHeight);
context.lineTo((width + pointerWidth) / 2, 0);
}
if(!cornerRadius) {
context.lineTo(width, 0);
} else {
context.lineTo(width - cornerRadius, 0);
context.arc(width - cornerRadius, cornerRadius, cornerRadius, Math.PI * 3 / 2, 0, false);
}
if (pointerDirection === RIGHT) {
context.lineTo(width, (height - pointerHeight) / 2);
context.lineTo(width + pointerWidth, height / 2);
context.lineTo(width, (height + pointerHeight) / 2);
}
if(!cornerRadius) {
context.lineTo(width, height);
} else {
context.lineTo(width, height - cornerRadius);
context.arc(width - cornerRadius, height - cornerRadius, cornerRadius, 0, Math.PI / 2, false);
}
if (pointerDirection === DOWN) {
context.lineTo((width + pointerWidth) / 2, height);
context.lineTo(width / 2, height + pointerHeight);
context.lineTo((width - pointerWidth) / 2, height);
}
if(!cornerRadius) {
context.lineTo(0, height);
} else {
context.lineTo(cornerRadius, height);
context.arc(cornerRadius, height - cornerRadius, cornerRadius, Math.PI / 2, Math.PI, false);
}
if (pointerDirection === LEFT) {
context.lineTo(0, (height + pointerHeight) / 2);
context.lineTo(-1 * pointerWidth, height / 2);
context.lineTo(0, (height - pointerHeight) / 2);
}
if(cornerRadius) {
context.lineTo(0, cornerRadius);
context.arc(cornerRadius, cornerRadius, cornerRadius, Math.PI, Math.PI * 3 / 2, false);
}
context.closePath();
context.fillStrokeShape(this);
},
getSelfRect: function() {
var x = 0,
y = 0,
pointerWidth = this.getPointerWidth(),
pointerHeight = this.getPointerHeight(),
direction = this.pointerDirection(),
width = this.getWidth(),
height = this.getHeight();
if (direction === UP) {
y -= pointerHeight;
height += pointerHeight;
} else if (direction === DOWN) {
height += pointerHeight;
} else if (direction === LEFT) {
// ARGH!!! I have no idea why should I used magic 1.5!!!!!!!!!
x -= pointerWidth * 1.5;
width += pointerWidth;
} else if (direction === RIGHT) {
width += pointerWidth * 1.5;
}
return {
x: x,
y: y,
width: width,
height: height
};
}
};
Konva.Util.extend(Konva.Tag, Konva.Shape);
Konva.Factory.addGetterSetter(Konva.Tag, 'pointerDirection', NONE);
/**
* set pointer Direction
* @name setPointerDirection
* @method
* @memberof Konva.Tag.prototype
* @param {String} pointerDirection can be up, right, down, left, or none. The
* default is none
*/
/**
* get pointer Direction
* @name getPointerDirection
* @method
* @memberof Konva.Tag.prototype
*/
Konva.Factory.addGetterSetter(Konva.Tag, 'pointerWidth', 0);
/**
* set pointer width
* @name setPointerWidth
* @method
* @memberof Konva.Tag.prototype
* @param {Number} pointerWidth
*/
/**
* get pointer width
* @name getPointerWidth
* @method
* @memberof Konva.Tag.prototype
*/
Konva.Factory.addGetterSetter(Konva.Tag, 'pointerHeight', 0);
/**
* set pointer height
* @name setPointerHeight
* @method
* @memberof Konva.Tag.prototype
* @param {Number} pointerHeight
*/
/**
* get pointer height
* @name getPointerHeight
* @method
* @memberof Konva.Tag.prototype
*/
Konva.Factory.addGetterSetter(Konva.Tag, 'cornerRadius', 0);
/**
* set corner radius
* @name setCornerRadius
* @method
* @memberof Konva.Tag.prototype
* @param {Number} corner radius
*/
/**
* get corner radius
* @name getCornerRadius
* @method
* @memberof Konva.Tag.prototype
*/
Konva.Collection.mapMethods(Konva.Tag);
})();
/*eslint-disable no-shadow, max-len, max-depth */
(function () {
'use strict';
/**
* Path constructor.
* @author Jason Follas
* @constructor
* @memberof Konva
* @augments Konva.Shape
* @param {Object} config
* @param {String} config.data SVG data string
* @@shapeParams
* @@nodeParams
* @example
* var path = new Konva.Path({
* x: 240,
* y: 40,
* data: 'M12.582,9.551C3.251,16.237,0.921,29.021,7.08,38.564l-2.36,1.689l4.893,2.262l4.893,2.262l-0.568-5.36l-0.567-5.359l-2.365,1.694c-4.657-7.375-2.83-17.185,4.352-22.33c7.451-5.338,17.817-3.625,23.156,3.824c5.337,7.449,3.625,17.813-3.821,23.152l2.857,3.988c9.617-6.893,11.827-20.277,4.935-29.896C35.591,4.87,22.204,2.658,12.582,9.551z',
* fill: 'green',
* scale: 2
* });
*/
Konva.Path = function (config) {
this.___init(config);
};
Konva.Path.prototype = {
___init: function (config) {
this.dataArray = [];
var that = this;
// call super constructor
Konva.Shape.call(this, config);
this.className = 'Path';
this.dataArray = Konva.Path.parsePathData(this.getData());
this.on('dataChange.konva', function () {
that.dataArray = Konva.Path.parsePathData(this.getData());
});
this.sceneFunc(this._sceneFunc);
},
_sceneFunc: function(context) {
var ca = this.dataArray;
// context position
context.beginPath();
for (var n = 0; n < ca.length; n++) {
var c = ca[n].command;
var p = ca[n].points;
switch (c) {
case 'L':
context.lineTo(p[0], p[1]);
break;
case 'M':
context.moveTo(p[0], p[1]);
break;
case 'C':
context.bezierCurveTo(p[0], p[1], p[2], p[3], p[4], p[5]);
break;
case 'Q':
context.quadraticCurveTo(p[0], p[1], p[2], p[3]);
break;
case 'A':
var cx = p[0], cy = p[1], rx = p[2], ry = p[3], theta = p[4], dTheta = p[5], psi = p[6], fs = p[7];
var r = (rx > ry) ? rx : ry;
var scaleX = (rx > ry) ? 1 : rx / ry;
var scaleY = (rx > ry) ? ry / rx : 1;
context.translate(cx, cy);
context.rotate(psi);
context.scale(scaleX, scaleY);
context.arc(0, 0, r, theta, theta + dTheta, 1 - fs);
context.scale(1 / scaleX, 1 / scaleY);
context.rotate(-psi);
context.translate(-cx, -cy);
break;
case 'z':
context.closePath();
break;
}
}
context.fillStrokeShape(this);
},
getSelfRect: function() {
var points = [];
this.dataArray.forEach(function(data) {
points = points.concat(data.points);
});
var minX = points[0];
var maxX = points[0];
var minY = points[1];
var maxY = points[1];
var x, y;
for (var i = 0; i < points.length / 2; i++) {
x = points[i * 2]; y = points[i * 2 + 1];
minX = Math.min(minX, x);
maxX = Math.max(maxX, x);
minY = Math.min(minY, y);
maxY = Math.max(maxY, y);
}
return {
x: Math.round(minX),
y: Math.round(minY),
width: Math.round(maxX - minX),
height: Math.round(maxY - minY)
};
}
};
Konva.Util.extend(Konva.Path, Konva.Shape);
Konva.Path.getLineLength = function(x1, y1, x2, y2) {
return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
};
Konva.Path.getPointOnLine = function(dist, P1x, P1y, P2x, P2y, fromX, fromY) {
if(fromX === undefined) {
fromX = P1x;
}
if(fromY === undefined) {
fromY = P1y;
}
var m = (P2y - P1y) / ((P2x - P1x) + 0.00000001);
var run = Math.sqrt(dist * dist / (1 + m * m));
if(P2x < P1x) {
run *= -1;
}
var rise = m * run;
var pt;
if (P2x === P1x) { // vertical line
pt = {
x: fromX,
y: fromY + rise
};
} else if((fromY - P1y) / ((fromX - P1x) + 0.00000001) === m) {
pt = {
x: fromX + run,
y: fromY + rise
};
}
else {
var ix, iy;
var len = this.getLineLength(P1x, P1y, P2x, P2y);
if(len < 0.00000001) {
return undefined;
}
var u = (((fromX - P1x) * (P2x - P1x)) + ((fromY - P1y) * (P2y - P1y)));
u = u / (len * len);
ix = P1x + u * (P2x - P1x);
iy = P1y + u * (P2y - P1y);
var pRise = this.getLineLength(fromX, fromY, ix, iy);
var pRun = Math.sqrt(dist * dist - pRise * pRise);
run = Math.sqrt(pRun * pRun / (1 + m * m));
if(P2x < P1x) {
run *= -1;
}
rise = m * run;
pt = {
x: ix + run,
y: iy + rise
};
}
return pt;
};
Konva.Path.getPointOnCubicBezier = function(pct, P1x, P1y, P2x, P2y, P3x, P3y, P4x, P4y) {
function CB1(t) {
return t * t * t;
}
function CB2(t) {
return 3 * t * t * (1 - t);
}
function CB3(t) {
return 3 * t * (1 - t) * (1 - t);
}
function CB4(t) {
return (1 - t) * (1 - t) * (1 - t);
}
var x = P4x * CB1(pct) + P3x * CB2(pct) + P2x * CB3(pct) + P1x * CB4(pct);
var y = P4y * CB1(pct) + P3y * CB2(pct) + P2y * CB3(pct) + P1y * CB4(pct);
return {
x: x,
y: y
};
};
Konva.Path.getPointOnQuadraticBezier = function(pct, P1x, P1y, P2x, P2y, P3x, P3y) {
function QB1(t) {
return t * t;
}
function QB2(t) {
return 2 * t * (1 - t);
}
function QB3(t) {
return (1 - t) * (1 - t);
}
var x = P3x * QB1(pct) + P2x * QB2(pct) + P1x * QB3(pct);
var y = P3y * QB1(pct) + P2y * QB2(pct) + P1y * QB3(pct);
return {
x: x,
y: y
};
};
Konva.Path.getPointOnEllipticalArc = function(cx, cy, rx, ry, theta, psi) {
var cosPsi = Math.cos(psi), sinPsi = Math.sin(psi);
var pt = {
x: rx * Math.cos(theta),
y: ry * Math.sin(theta)
};
return {
x: cx + (pt.x * cosPsi - pt.y * sinPsi),
y: cy + (pt.x * sinPsi + pt.y * cosPsi)
};
};
/*
* get parsed data array from the data
* string. V, v, H, h, and l data are converted to
* L data for the purpose of high performance Path
* rendering
*/
Konva.Path.parsePathData = function(data) {
// Path Data Segment must begin with a moveTo
//m (x y)+ Relative moveTo (subsequent points are treated as lineTo)
//M (x y)+ Absolute moveTo (subsequent points are treated as lineTo)
//l (x y)+ Relative lineTo
//L (x y)+ Absolute LineTo
//h (x)+ Relative horizontal lineTo
//H (x)+ Absolute horizontal lineTo
//v (y)+ Relative vertical lineTo
//V (y)+ Absolute vertical lineTo
//z (closepath)
//Z (closepath)
//c (x1 y1 x2 y2 x y)+ Relative Bezier curve
//C (x1 y1 x2 y2 x y)+ Absolute Bezier curve
//q (x1 y1 x y)+ Relative Quadratic Bezier
//Q (x1 y1 x y)+ Absolute Quadratic Bezier
//t (x y)+ Shorthand/Smooth Relative Quadratic Bezier
//T (x y)+ Shorthand/Smooth Absolute Quadratic Bezier
//s (x2 y2 x y)+ Shorthand/Smooth Relative Bezier curve
//S (x2 y2 x y)+ Shorthand/Smooth Absolute Bezier curve
//a (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+ Relative Elliptical Arc
//A (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+ Absolute Elliptical Arc
// return early if data is not defined
if(!data) {
return [];
}
// command string
var cs = data;
// command chars
var cc = ['m', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z', 'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A'];
// convert white spaces to commas
cs = cs.replace(new RegExp(' ', 'g'), ',');
// create pipes so that we can split the data
for(var n = 0; n < cc.length; n++) {
cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
}
// create array
var arr = cs.split('|');
var ca = [];
// init context point
var cpx = 0;
var cpy = 0;
for( n = 1; n < arr.length; n++) {
var str = arr[n];
var c = str.charAt(0);
str = str.slice(1);
// remove ,- for consistency
str = str.replace(new RegExp(',-', 'g'), '-');
// add commas so that it's easy to split
str = str.replace(new RegExp('-', 'g'), ',-');
str = str.replace(new RegExp('e,-', 'g'), 'e-');
var p = str.split(',');
if(p.length > 0 && p[0] === '') {
p.shift();
}
// convert strings to floats
for(var i = 0; i < p.length; i++) {
p[i] = parseFloat(p[i]);
}
while(p.length > 0) {
if(isNaN(p[0])) {// case for a trailing comma before next command
break;
}
var cmd = null;
var points = [];
var startX = cpx, startY = cpy;
// Move var from within the switch to up here (jshint)
var prevCmd, ctlPtx, ctlPty; // Ss, Tt
var rx, ry, psi, fa, fs, x1, y1; // Aa
// convert l, H, h, V, and v to L
switch (c) {
// Note: Keep the lineTo's above the moveTo's in this switch
case 'l':
cpx += p.shift();
cpy += p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'L':
cpx = p.shift();
cpy = p.shift();
points.push(cpx, cpy);
break;
// Note: lineTo handlers need to be above this point
case 'm':
var dx = p.shift();
var dy = p.shift();
cpx += dx;
cpy += dy;
cmd = 'M';
// After closing the path move the current position
// to the the first point of the path (if any).
if(ca.length > 2 && ca[ca.length - 1].command === 'z'){
for(var idx = ca.length - 2; idx >= 0; idx--){
if(ca[idx].command === 'M'){
cpx = ca[idx].points[0] + dx;
cpy = ca[idx].points[1] + dy;
break;
}
}
}
points.push(cpx, cpy);
c = 'l';
// subsequent points are treated as relative lineTo
break;
case 'M':
cpx = p.shift();
cpy = p.shift();
cmd = 'M';
points.push(cpx, cpy);
c = 'L';
// subsequent points are treated as absolute lineTo
break;
case 'h':
cpx += p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'H':
cpx = p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'v':
cpy += p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'V':
cpy = p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'C':
points.push(p.shift(), p.shift(), p.shift(), p.shift());
cpx = p.shift();
cpy = p.shift();
points.push(cpx, cpy);
break;
case 'c':
points.push(cpx + p.shift(), cpy + p.shift(), cpx + p.shift(), cpy + p.shift());
cpx += p.shift();
cpy += p.shift();
cmd = 'C';
points.push(cpx, cpy);
break;
case 'S':
ctlPtx = cpx;
ctlPty = cpy;
prevCmd = ca[ca.length - 1];
if(prevCmd.command === 'C') {
ctlPtx = cpx + (cpx - prevCmd.points[2]);
ctlPty = cpy + (cpy - prevCmd.points[3]);
}
points.push(ctlPtx, ctlPty, p.shift(), p.shift());
cpx = p.shift();
cpy = p.shift();
cmd = 'C';
points.push(cpx, cpy);
break;
case 's':
ctlPtx = cpx;
ctlPty = cpy;
prevCmd = ca[ca.length - 1];
if(prevCmd.command === 'C') {
ctlPtx = cpx + (cpx - prevCmd.points[2]);
ctlPty = cpy + (cpy - prevCmd.points[3]);
}
points.push(ctlPtx, ctlPty, cpx + p.shift(), cpy + p.shift());
cpx += p.shift();
cpy += p.shift();
cmd = 'C';
points.push(cpx, cpy);
break;
case 'Q':
points.push(p.shift(), p.shift());
cpx = p.shift();
cpy = p.shift();
points.push(cpx, cpy);
break;
case 'q':
points.push(cpx + p.shift(), cpy + p.shift());
cpx += p.shift();
cpy += p.shift();
cmd = 'Q';
points.push(cpx, cpy);
break;
case 'T':
ctlPtx = cpx;
ctlPty = cpy;
prevCmd = ca[ca.length - 1];
if(prevCmd.command === 'Q') {
ctlPtx = cpx + (cpx - prevCmd.points[0]);
ctlPty = cpy + (cpy - prevCmd.points[1]);
}
cpx = p.shift();
cpy = p.shift();
cmd = 'Q';
points.push(ctlPtx, ctlPty, cpx, cpy);
break;
case 't':
ctlPtx = cpx;
ctlPty = cpy;
prevCmd = ca[ca.length - 1];
if(prevCmd.command === 'Q') {
ctlPtx = cpx + (cpx - prevCmd.points[0]);
ctlPty = cpy + (cpy - prevCmd.points[1]);
}
cpx += p.shift();
cpy += p.shift();
cmd = 'Q';
points.push(ctlPtx, ctlPty, cpx, cpy);
break;
case 'A':
rx = p.shift();
ry = p.shift();
psi = p.shift();
fa = p.shift();
fs = p.shift();
x1 = cpx;
y1 = cpy;
cpx = p.shift();
cpy = p.shift();
cmd = 'A';
points = this.convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
break;
case 'a':
rx = p.shift();
ry = p.shift();
psi = p.shift();
fa = p.shift();
fs = p.shift();
x1 = cpx;
y1 = cpy; cpx += p.shift();
cpy += p.shift();
cmd = 'A';
points = this.convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
break;
}
ca.push({
command: cmd || c,
points: points,
start: {
x: startX,
y: startY
},
pathLength: this.calcLength(startX, startY, cmd || c, points)
});
}
if(c === 'z' || c === 'Z') {
ca.push({
command: 'z',
points: [],
start: undefined,
pathLength: 0
});
}
}
return ca;
};
Konva.Path.calcLength = function(x, y, cmd, points) {
var len, p1, p2, t;
var path = Konva.Path;
switch (cmd) {
case 'L':
return path.getLineLength(x, y, points[0], points[1]);
case 'C':
// Approximates by breaking curve into 100 line segments
len = 0.0;
p1 = path.getPointOnCubicBezier(0, x, y, points[0], points[1], points[2], points[3], points[4], points[5]);
for( t = 0.01; t <= 1; t += 0.01) {
p2 = path.getPointOnCubicBezier(t, x, y, points[0], points[1], points[2], points[3], points[4], points[5]);
len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
return len;
case 'Q':
// Approximates by breaking curve into 100 line segments
len = 0.0;
p1 = path.getPointOnQuadraticBezier(0, x, y, points[0], points[1], points[2], points[3]);
for( t = 0.01; t <= 1; t += 0.01) {
p2 = path.getPointOnQuadraticBezier(t, x, y, points[0], points[1], points[2], points[3]);
len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
return len;
case 'A':
// Approximates by breaking curve into line segments
len = 0.0;
var start = points[4];
// 4 = theta
var dTheta = points[5];
// 5 = dTheta
var end = points[4] + dTheta;
var inc = Math.PI / 180.0;
// 1 degree resolution
if(Math.abs(start - end) < inc) {
inc = Math.abs(start - end);
}
// Note: for purpose of calculating arc length, not going to worry about rotating X-axis by angle psi
p1 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], start, 0);
if(dTheta < 0) {// clockwise
for( t = start - inc; t > end; t -= inc) {
p2 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
}
else {// counter-clockwise
for( t = start + inc; t < end; t += inc) {
p2 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
}
p2 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], end, 0);
len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
return len;
}
return 0;
};
Konva.Path.convertEndpointToCenterParameterization = function(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg) {
// Derived from: http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
var psi = psiDeg * (Math.PI / 180.0);
var xp = Math.cos(psi) * (x1 - x2) / 2.0 + Math.sin(psi) * (y1 - y2) / 2.0;
var yp = -1 * Math.sin(psi) * (x1 - x2) / 2.0 + Math.cos(psi) * (y1 - y2) / 2.0;
var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);
if(lambda > 1) {
rx *= Math.sqrt(lambda);
ry *= Math.sqrt(lambda);
}
var f = Math.sqrt((((rx * rx) * (ry * ry)) - ((rx * rx) * (yp * yp)) - ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp) + (ry * ry) * (xp * xp)));
if(fa === fs) {
f *= -1;
}
if(isNaN(f)) {
f = 0;
}
var cxp = f * rx * yp / ry;
var cyp = f * -ry * xp / rx;
var cx = (x1 + x2) / 2.0 + Math.cos(psi) * cxp - Math.sin(psi) * cyp;
var cy = (y1 + y2) / 2.0 + Math.sin(psi) * cxp + Math.cos(psi) * cyp;
var vMag = function(v) {
return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
};
var vRatio = function(u, v) {
return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
};
var vAngle = function(u, v) {
return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) * Math.acos(vRatio(u, v));
};
var theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]);
var u = [(xp - cxp) / rx, (yp - cyp) / ry];
var v = [(-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry];
var dTheta = vAngle(u, v);
if(vRatio(u, v) <= -1) {
dTheta = Math.PI;
}
if(vRatio(u, v) >= 1) {
dTheta = 0;
}
if(fs === 0 && dTheta > 0) {
dTheta = dTheta - 2 * Math.PI;
}
if(fs === 1 && dTheta < 0) {
dTheta = dTheta + 2 * Math.PI;
}
return [cx, cy, rx, ry, theta, dTheta, psi, fs];
};
// add getters setters
Konva.Factory.addGetterSetter(Konva.Path, 'data');
/**
* set SVG path data string. This method
* also automatically parses the data string
* into a data array. Currently supported SVG data:
* M, m, L, l, H, h, V, v, Q, q, T, t, C, c, S, s, A, a, Z, z
* @name setData
* @method
* @memberof Konva.Path.prototype
* @param {String} SVG path command string
*/
/**
* get SVG path data string
* @name getData
* @method
* @memberof Konva.Path.prototype
*/
Konva.Collection.mapMethods(Konva.Path);
})();
(function() {
'use strict';
/**
* RegularPolygon constructor.&nbsp; Examples include triangles, squares, pentagons, hexagons, etc.
* @constructor
* @memberof Konva
* @augments Konva.Shape
* @param {Object} config
* @param {Number} config.sides
* @param {Number} config.radius
* @@shapeParams
* @@nodeParams
* @example
* var hexagon = new Konva.RegularPolygon({
* x: 100,
* y: 200,
* sides: 6,
* radius: 70,
* fill: 'red',
* stroke: 'black',
* strokeWidth: 4
* });
*/
Konva.RegularPolygon = function(config) {
this.___init(config);
};
Konva.RegularPolygon.prototype = {
_centroid: true,
___init: function(config) {
// call super constructor
Konva.Shape.call(this, config);
this.className = 'RegularPolygon';
this.sceneFunc(this._sceneFunc);
},
_sceneFunc: function(context) {
var sides = this.attrs.sides,
radius = this.attrs.radius,
n, x, y;
context.beginPath();
context.moveTo(0, 0 - radius);
for(n = 1; n < sides; n++) {
x = radius * Math.sin(n * 2 * Math.PI / sides);
y = -1 * radius * Math.cos(n * 2 * Math.PI / sides);
context.lineTo(x, y);
}
context.closePath();
context.fillStrokeShape(this);
},
getWidth: function() {
return this.getRadius() * 2;
},
// implements Shape.prototype.getHeight()
getHeight: function() {
return this.getRadius() * 2;
},
// implements Shape.prototype.setWidth()
setWidth: function(width) {
Konva.Node.prototype.setWidth.call(this, width);
if (this.radius() !== width / 2) {
this.setRadius(width / 2);
}
},
// implements Shape.prototype.setHeight()
setHeight: function(height) {
Konva.Node.prototype.setHeight.call(this, height);
if (this.radius() !== height / 2) {
this.setRadius(height / 2);
}
}
};
Konva.Util.extend(Konva.RegularPolygon, Konva.Shape);
// add getters setters
Konva.Factory.addGetterSetter(Konva.RegularPolygon, 'radius', 0);
/**
* set radius
* @name setRadius
* @method
* @memberof Konva.RegularPolygon.prototype
* @param {Number} radius
*/
/**
* get radius
* @name getRadius
* @method
* @memberof Konva.RegularPolygon.prototype
*/
Konva.Factory.addGetterSetter(Konva.RegularPolygon, 'sides', 0);
/**
* set number of sides
* @name setSides
* @method
* @memberof Konva.RegularPolygon.prototype
* @param {int} sides
*/
/**
* get number of sides
* @name getSides
* @method
* @memberof Konva.RegularPolygon.prototype
*/
Konva.Collection.mapMethods(Konva.RegularPolygon);
})();
(function() {
'use strict';
/**
* Star constructor
* @constructor
* @memberof Konva
* @augments Konva.Shape
* @param {Object} config
* @param {Integer} config.numPoints
* @param {Number} config.innerRadius
* @param {Number} config.outerRadius
* @@shapeParams
* @@nodeParams
* @example
* var star = new Konva.Star({
* x: 100,
* y: 200,
* numPoints: 5,
* innerRadius: 70,
* outerRadius: 70,
* fill: 'red',
* stroke: 'black',
* strokeWidth: 4
* });
*/
Konva.Star = function(config) {
this.___init(config);
};
Konva.Star.prototype = {
_centroid: true,
___init: function(config) {
// call super constructor
Konva.Shape.call(this, config);
this.className = 'Star';
this.sceneFunc(this._sceneFunc);
},
_sceneFunc: function(context) {
var innerRadius = this.innerRadius(),
outerRadius = this.outerRadius(),
numPoints = this.numPoints();
context.beginPath();
context.moveTo(0, 0 - outerRadius);
for(var n = 1; n < numPoints * 2; n++) {
var radius = n % 2 === 0 ? outerRadius : innerRadius;
var x = radius * Math.sin(n * Math.PI / numPoints);
var y = -1 * radius * Math.cos(n * Math.PI / numPoints);
context.lineTo(x, y);
}
context.closePath();
context.fillStrokeShape(this);
},
// implements Shape.prototype.getWidth()
getWidth: function() {
return this.getOuterRadius() * 2;
},
// implements Shape.prototype.getHeight()
getHeight: function() {
return this.getOuterRadius() * 2;
},
// implements Shape.prototype.setWidth()
setWidth: function(width) {
Konva.Node.prototype.setWidth.call(this, width);
if (this.outerRadius() !== width / 2) {
this.setOuterRadius(width / 2);
}
},
// implements Shape.prototype.setHeight()
setHeight: function(height) {
Konva.Node.prototype.setHeight.call(this, height);
if (this.outerRadius() !== height / 2) {
this.setOuterRadius(height / 2);
}
}
};
Konva.Util.extend(Konva.Star, Konva.Shape);
// add getters setters
Konva.Factory.addGetterSetter(Konva.Star, 'numPoints', 5);
/**
* set number of points
* @name setNumPoints
* @method
* @memberof Konva.Star.prototype
* @param {Integer} points
*/
/**
* get number of points
* @name getNumPoints
* @method
* @memberof Konva.Star.prototype
*/
Konva.Factory.addGetterSetter(Konva.Star, 'innerRadius', 0);
/**
* set inner radius
* @name setInnerRadius
* @method
* @memberof Konva.Star.prototype
* @param {Number} radius
*/
/**
* get inner radius
* @name getInnerRadius
* @method
* @memberof Konva.Star.prototype
*/
Konva.Factory.addGetterSetter(Konva.Star, 'outerRadius', 0);
/**
* set outer radius
* @name setOuterRadius
* @method
* @memberof Konva.Star.prototype
* @param {Number} radius
*/
/**
* get outer radius
* @name getOuterRadius
* @method
* @memberof Konva.Star.prototype
*/
Konva.Collection.mapMethods(Konva.Star);
})();
(function() {
'use strict';
var EMPTY_STRING = '',
//CALIBRI = 'Calibri',
NORMAL = 'normal';
/**
* Path constructor.
* @author Jason Follas
* @constructor
* @memberof Konva
* @augments Konva.Shape
* @param {Object} config
* @param {String} [config.fontFamily] default is Calibri
* @param {Number} [config.fontSize] default is 12
* @param {String} [config.fontStyle] can be normal, bold, or italic. Default is normal
* @param {String} [config.fontVariant] can be normal or small-caps. Default is normal
* @param {String} config.text
* @param {String} config.data SVG data string
* @@shapeParams
* @@nodeParams
* @example
* var textpath = new Konva.TextPath({
* x: 100,
* y: 50,
* fill: '#333',
* fontSize: '24',
* fontFamily: 'Arial',
* text: 'All the world\'s a stage, and all the men and women merely players.',
* data: 'M10,10 C0,0 10,150 100,100 S300,150 400,50'
* });
*/
Konva.TextPath = function(config) {
this.___init(config);
};
function _fillFunc(context) {
context.fillText(this.partialText, 0, 0);
}
function _strokeFunc(context) {
context.strokeText(this.partialText, 0, 0);
}
Konva.TextPath.prototype = {
___init: function(config) {
var that = this;
this.dummyCanvas = Konva.Util.createCanvasElement();
this.dataArray = [];
// call super constructor
Konva.Shape.call(this, config);
// overrides
// TODO: shouldn't this be on the prototype?
this._fillFunc = _fillFunc;
this._strokeFunc = _strokeFunc;
this._fillFuncHit = _fillFunc;
this._strokeFuncHit = _strokeFunc;
this.className = 'TextPath';
this.dataArray = Konva.Path.parsePathData(this.attrs.data);
this.on('dataChange.konva', function() {
that.dataArray = Konva.Path.parsePathData(this.attrs.data);
});
// update text data for certain attr changes
this.on('textChange.konva textStroke.konva textStrokeWidth.konva', that._setTextData);
that._setTextData();
this.sceneFunc(this._sceneFunc);
},
_sceneFunc: function(context) {
context.setAttr('font', this._getContextFont());
context.setAttr('textBaseline', 'middle');
context.setAttr('textAlign', 'left');
context.save();
var glyphInfo = this.glyphInfo;
for(var i = 0; i < glyphInfo.length; i++) {
context.save();
var p0 = glyphInfo[i].p0;
context.translate(p0.x, p0.y);
context.rotate(glyphInfo[i].rotation);
this.partialText = glyphInfo[i].text;
context.fillStrokeShape(this);
context.restore();
//// To assist with debugging visually, uncomment following
// context.beginPath();
// if (i % 2)
// context.strokeStyle = 'cyan';
// else
// context.strokeStyle = 'green';
// var p1 = glyphInfo[i].p1;
// context.moveTo(p0.x, p0.y);
// context.lineTo(p1.x, p1.y);
// context.stroke();
}
context.restore();
},
/**
* get text width in pixels
* @method
* @memberof Konva.TextPath.prototype
*/
getTextWidth: function() {
return this.textWidth;
},
/**
* get text height in pixels
* @method
* @memberof Konva.TextPath.prototype
*/
getTextHeight: function() {
return this.textHeight;
},
/**
* set text
* @method
* @memberof Konva.TextPath.prototype
* @param {String} text
*/
setText: function(text) {
Konva.Text.prototype.setText.call(this, text);
},
_getTextSize: function(text) {
var dummyCanvas = this.dummyCanvas;
var _context = dummyCanvas.getContext('2d');
_context.save();
_context.font = this._getContextFont();
var metrics = _context.measureText(text);
_context.restore();
return {
width: metrics.width,
height: parseInt(this.attrs.fontSize, 10)
};
},
_setTextData: function() {
var that = this;
var size = this._getTextSize(this.attrs.text);
this.textWidth = size.width;
this.textHeight = size.height;
this.glyphInfo = [];
var charArr = this.attrs.text.split('');
var p0, p1, pathCmd;
var pIndex = -1;
var currentT = 0;
var getNextPathSegment = function() {
currentT = 0;
var pathData = that.dataArray;
for(var j = pIndex + 1; j < pathData.length; j++) {
if(pathData[j].pathLength > 0) {
pIndex = j;
return pathData[j];
}
else if(pathData[j].command === 'M') {
p0 = {
x: pathData[j].points[0],
y: pathData[j].points[1]
};
}
}
return {};
};
var findSegmentToFitCharacter = function(c) {
var glyphWidth = that._getTextSize(c).width;
var currLen = 0;
var attempts = 0;
p1 = undefined;
while(Math.abs(glyphWidth - currLen) / glyphWidth > 0.01 && attempts < 25) {
attempts++;
var cumulativePathLength = currLen;
while(pathCmd === undefined) {
pathCmd = getNextPathSegment();
if(pathCmd && cumulativePathLength + pathCmd.pathLength < glyphWidth) {
cumulativePathLength += pathCmd.pathLength;
pathCmd = undefined;
}
}
if(pathCmd === {} || p0 === undefined) {
return undefined;
}
var needNewSegment = false;
switch (pathCmd.command) {
case 'L':
if(Konva.Path.getLineLength(p0.x, p0.y, pathCmd.points[0], pathCmd.points[1]) > glyphWidth) {
p1 = Konva.Path.getPointOnLine(glyphWidth, p0.x, p0.y, pathCmd.points[0], pathCmd.points[1], p0.x, p0.y);
}
else {
pathCmd = undefined;
}
break;
case 'A':
var start = pathCmd.points[4];
// 4 = theta
var dTheta = pathCmd.points[5];
// 5 = dTheta
var end = pathCmd.points[4] + dTheta;
if(currentT === 0){
currentT = start + 0.00000001;
}
// Just in case start is 0
else if(glyphWidth > currLen) {
currentT += (Math.PI / 180.0) * dTheta / Math.abs(dTheta);
}
else {
currentT -= Math.PI / 360.0 * dTheta / Math.abs(dTheta);
}
// Credit for bug fix: @therth https://github.com/ericdrowell/KonvaJS/issues/249
// Old code failed to render text along arc of this path: "M 50 50 a 150 50 0 0 1 250 50 l 50 0"
if(dTheta < 0 && currentT < end || dTheta >= 0 && currentT > end) {
currentT = end;
needNewSegment = true;
}
p1 = Konva.Path.getPointOnEllipticalArc(pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3], currentT, pathCmd.points[6]);
break;
case 'C':
if(currentT === 0) {
if(glyphWidth > pathCmd.pathLength) {
currentT = 0.00000001;
}
else {
currentT = glyphWidth / pathCmd.pathLength;
}
}
else if(glyphWidth > currLen) {
currentT += (glyphWidth - currLen) / pathCmd.pathLength;
}
else {
currentT -= (currLen - glyphWidth) / pathCmd.pathLength;
}
if(currentT > 1.0) {
currentT = 1.0;
needNewSegment = true;
}
p1 = Konva.Path.getPointOnCubicBezier(currentT, pathCmd.start.x, pathCmd.start.y, pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3], pathCmd.points[4], pathCmd.points[5]);
break;
case 'Q':
if(currentT === 0) {
currentT = glyphWidth / pathCmd.pathLength;
}
else if(glyphWidth > currLen) {
currentT += (glyphWidth - currLen) / pathCmd.pathLength;
}
else {
currentT -= (currLen - glyphWidth) / pathCmd.pathLength;
}
if(currentT > 1.0) {
currentT = 1.0;
needNewSegment = true;
}
p1 = Konva.Path.getPointOnQuadraticBezier(currentT, pathCmd.start.x, pathCmd.start.y, pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3]);
break;
}
if(p1 !== undefined) {
currLen = Konva.Path.getLineLength(p0.x, p0.y, p1.x, p1.y);
}
if(needNewSegment) {
needNewSegment = false;
pathCmd = undefined;
}
}
};
for(var i = 0; i < charArr.length; i++) {
// Find p1 such that line segment between p0 and p1 is approx. width of glyph
findSegmentToFitCharacter(charArr[i]);
if(p0 === undefined || p1 === undefined) {
break;
}
var width = Konva.Path.getLineLength(p0.x, p0.y, p1.x, p1.y);
// Note: Since glyphs are rendered one at a time, any kerning pair data built into the font will not be used.
// Can foresee having a rough pair table built in that the developer can override as needed.
var kern = 0;
// placeholder for future implementation
var midpoint = Konva.Path.getPointOnLine(kern + width / 2.0, p0.x, p0.y, p1.x, p1.y);
var rotation = Math.atan2((p1.y - p0.y), (p1.x - p0.x));
this.glyphInfo.push({
transposeX: midpoint.x,
transposeY: midpoint.y,
text: charArr[i],
rotation: rotation,
p0: p0,
p1: p1
});
p0 = p1;
}
},
getSelfRect: function() {
var points = [];
var fontSize = this.fontSize();
this.glyphInfo.forEach(function(info) {
points.push(info.p0.x);
points.push(info.p0.y);
points.push(info.p1.x);
points.push(info.p1.y);
});
var minX = points[0];
var maxX = points[0];
var minY = points[0];
var maxY = points[0];
var x, y;
for (var i = 0; i < points.length / 2; i++) {
x = points[i * 2]; y = points[i * 2 + 1];
minX = Math.min(minX, x);
maxX = Math.max(maxX, x);
minY = Math.min(minY, y);
maxY = Math.max(maxY, y);
}
return {
x: Math.round(minX) - fontSize,
y: Math.round(minY) - fontSize,
width: Math.round(maxX - minX) + fontSize * 2,
height: Math.round(maxY - minY) + fontSize * 2
};
}
};
// map TextPath methods to Text
Konva.TextPath.prototype._getContextFont = Konva.Text.prototype._getContextFont;
Konva.Util.extend(Konva.TextPath, Konva.Shape);
// add setters and getters
Konva.Factory.addGetterSetter(Konva.TextPath, 'fontFamily', 'Arial');
/**
* set font family
* @name setFontFamily
* @method
* @memberof Konva.TextPath.prototype
* @param {String} fontFamily
*/
/**
* get font family
* @name getFontFamily
* @method
* @memberof Konva.TextPath.prototype
*/
Konva.Factory.addGetterSetter(Konva.TextPath, 'fontSize', 12);
/**
* set font size
* @name setFontSize
* @method
* @memberof Konva.TextPath.prototype
* @param {int} fontSize
*/
/**
* get font size
* @name getFontSize
* @method
* @memberof Konva.TextPath.prototype
*/
Konva.Factory.addGetterSetter(Konva.TextPath, 'fontStyle', NORMAL);
/**
* set font style. Can be 'normal', 'italic', or 'bold'. 'normal' is the default.
* @name setFontStyle
* @method
* @memberof Konva.TextPath.prototype
* @param {String} fontStyle
*/
/**
* get font style
* @name getFontStyle
* @method
* @memberof Konva.TextPath.prototype
*/
Konva.Factory.addGetterSetter(Konva.TextPath, 'fontVariant', NORMAL);
/**
* set font variant. Can be 'normal' or 'small-caps'. 'normal' is the default.
* @name setFontVariant
* @method
* @memberof Konva.TextPath.prototype
* @param {String} fontVariant
*/
/**
* @get font variant
* @name getFontVariant
* @method
* @memberof Konva.TextPath.prototype
*/
Konva.Factory.addGetter(Konva.TextPath, 'text', EMPTY_STRING);
/**
* get text
* @name getText
* @method
* @memberof Konva.TextPath.prototype
*/
Konva.Collection.mapMethods(Konva.TextPath);
})();
+12
-1

@@ -7,6 +7,17 @@ # Change Log

## [0.13.9][2016-05-14]
### Changed
- typescript definition in npm package
- node@5.10.1, canvas@1.3.14, jsdom@8.5.0 support
- `Konva.Path` will be filled when it is not closed
- `Animation.start()` will not not immediate sync draw. This should improve performance a little.
- Warning when node for `Tween` is not in layer yet.
- `removeChildren()` remove only first level children. So it will not remove grandchildren.
## [0.12.4][2016-04-19]
### Changed
- `batchDraw` will not do immediate `draw()`
- `batchDraw` will do not immediate `draw()`

@@ -13,0 +24,0 @@ ### Fixed

+14
-12
{
"name": "konva",
"version": "0.12.4",
"version": "0.13.0",
"author": "Anton Lavrenov",
"files": [
"README.md",
"konva.js",
"konva.min.js",
"src",
"konva.d.ts"
],
"main": "konva.js",
"typings": "./konva.d.ts",
"scripts": {
"start": "gulp",
"build": "gulp build",
"full-build": "gulp lint test build",

@@ -14,5 +24,5 @@ "test": "gulp test"

"gulp-concat": "^2.6.0",
"gulp-connect": "^3.0.0",
"gulp-connect": "^4.0.0",
"gulp-eslint": "^2.0.0",
"gulp-jscpd": "0.0.6",
"gulp-jscpd": "0.0.7",
"gulp-jsdoc": "^0.1.4",

@@ -41,3 +51,2 @@ "gulp-mocha-phantomjs": "^0.11.0",

"readmeFilename": "README.md",
"main": "konva.js",
"repository": {

@@ -47,10 +56,3 @@ "type": "git",

},
"license": "MIT",
"testling": {
"html": "test/runner.html",
"harness": "mocha-tdd",
"browsers": [
"ie/latest"
]
}
"license": "MIT"
}
+10
-10

@@ -23,3 +23,3 @@ ![Konva logo](https://raw.githubusercontent.com/konvajs/konvajs.github.io/master/apple-touch-icon-180x180.png)

```html
<script src="https://cdn.rawgit.com/konvajs/konva/0.12.4/konva.min.js"></script>
<script src="https://cdn.rawgit.com/konvajs/konva/0.13.0/konva.min.js"></script>
<div id="container"></div>

@@ -69,6 +69,6 @@ <script>

```html
<script src="https://cdn.rawgit.com/konvajs/konva/0.12.4/konva.min.js"></script>
<script src="https://cdn.rawgit.com/konvajs/konva/0.13.0/konva.min.js"></script>
```
You can use CDN: [https://cdn.rawgit.com/konvajs/konva/0.12.4/konva.min.js](https://cdn.rawgit.com/konvajs/konva/0.12.4/konva.min.js)
You can use CDN: [https://cdn.rawgit.com/konvajs/konva/0.13.0/konva.min.js](https://cdn.rawgit.com/konvajs/konva/0.13.0/konva.min.js)

@@ -86,3 +86,3 @@ ###2 Load via AMD (requirejs):

```bash
npm install konva
npm install konva --save
```

@@ -116,14 +116,14 @@

Support for node 0.12.x only for now.
You have to install some deps manually to use Konva in nodejs env.
We are using [node-canvas](https://github.com/LearnBoost/node-canvas) to create canvas element.
We are using [node-canvas](https://github.com/Automattic/node-canvas) to create canvas element.
1. Install node-canvas dependencies [https://github.com/LearnBoost/node-canvas/wiki/_pages](https://github.com/LearnBoost/node-canvas/wiki/_pages)
2. `npm install canvas@1.2.x`
2. `npm install jsdom@3.x` (jsdom v4 require iojs)
3. `npm install konva`
1. Install node-canvas dependencies [https://github.com/Automattic/node-canvas](https://github.com/Automattic/node-canvas)
2. `npm install canvas --save`
2. `npm install jsdom --save`
3. `npm install konva --save`
See file `resources/nodejs-demo.js` for example.
Last tested with node@5.10.1, canvas@1.3.14, jsdom@8.5.0

@@ -130,0 +130,0 @@ #Change log

@@ -256,4 +256,4 @@ (function(Konva) {

if(Anim.animations.length) {
Anim._runFrames();
requestAnimFrame(Anim._animationLoop);
Anim._runFrames();
}

@@ -267,3 +267,3 @@ else {

this.animRunning = true;
this._animationLoop();
requestAnimFrame(this._animationLoop);
}

@@ -270,0 +270,0 @@ };

@@ -72,5 +72,2 @@ (function() {

child.index = 0;
if (child.hasChildren()) {
child.removeChildren();
}
child.remove();

@@ -77,0 +74,0 @@ }

@@ -176,2 +176,25 @@

},
_detectIE: function(ua) {
var msie = ua.indexOf('msie ');
if (msie > 0) {
// IE 10 or older => return version number
return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
}
var trident = ua.indexOf('trident/');
if (trident > 0) {
// IE 11 => return version number
var rv = ua.indexOf('rv:');
return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
}
var edge = ua.indexOf('edge/');
if (edge > 0) {
// Edge (IE 12+) => return version number
return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
}
// other browser
return false;
},
_parseUA: function(userAgent) {

@@ -194,3 +217,3 @@ var ua = userAgent.toLowerCase(),

version: match[ 2 ] || '0',
isIE: Konva._detectIE(ua),
// adding mobile flab

@@ -213,2 +236,3 @@ mobile: mobile,

if (glob.Konva) {

@@ -236,4 +260,4 @@ console.error(

Konva.document = jsdom('<!DOCTYPE html><html><head></head><body></body></html>');
Konva.window = Konva.document.parentWindow;
Konva.window = jsdom('<!DOCTYPE html><html><head></head><body></body></html>').defaultView;
Konva.document = Konva.window.document;
Konva.window.Image = Canvas.Image;

@@ -240,0 +264,0 @@ Konva._nodeCanvas = Canvas;

@@ -211,3 +211,13 @@ /*eslint-disable max-depth */

_getContextFont: function() {
return this.getFontStyle() + SPACE + this.getFontVariant() + SPACE + this.getFontSize() + PX_SPACE + this.getFontFamily();
// IE don't want to work with usual font style
// bold was not working
// removing font variant will solve
// fix for: https://github.com/konvajs/konva/issues/94
if (Konva.UA.isIE) {
return this.getFontStyle() + SPACE + this.getFontSize() + PX_SPACE + this.getFontFamily();
}
return this.getFontStyle() + SPACE +
this.getFontVariant() + SPACE +
this.getFontSize() + PX_SPACE +
this.getFontFamily();
},

@@ -214,0 +224,0 @@ _addTextLine: function (line, width) {

@@ -175,5 +175,11 @@ (function() {

var layers = node.getLayer() || ((node instanceof Konva.Stage) ? node.getLayers() : null);
if (!layers) {
Konva.Util.error(
'Tween constructor have `node` that is not in a layer. Please add node into layer first.'
);
}
this.anim = new Konva.Animation(function() {
that.tween.onEnterFrame();
}, node.getLayer() || ((node instanceof Konva.Stage) ? node.getLayers() : null));
}, layers);

@@ -180,0 +186,0 @@ this.tween = new Tween(key, function(i) {

{
"env": {
"browser": true,
"node": true
},
"rules": {
"no-alert": 0,
"no-array-constructor": 2,
"no-bitwise": 0,
"no-caller": 2,
"no-catch-shadow": 2,
"comma-dangle": 2,
"no-underscore-dangle": 0,
"no-cond-assign": 2,
"no-console": 0,
"no-constant-condition": 0,
"no-continue": 0,
"no-control-regex": 2,
"no-debugger": 2,
"no-delete-var": 2,
"no-div-regex": 0,
"no-dupe-keys": 2,
"no-dupe-args": 2,
"no-duplicate-case": 2,
"no-else-return": 1,
"no-empty": 2,
"no-empty-class": 0,
"no-empty-character-class": 2,
"no-labels": 2,
"no-eq-null": 2,
"no-eval": 2,
"no-ex-assign": 2,
"no-extend-native": 2,
"no-extra-bind": 2,
"no-extra-boolean-cast": 2,
"no-extra-parens": 0,
"no-extra-semi": 2,
"no-fallthrough": 2,
"no-floating-decimal": 0,
"no-func-assign": 2,
"no-implied-eval": 2,
"no-inline-comments": 0,
"no-inner-declarations": [2, "functions"],
"no-invalid-regexp": 2,
"no-irregular-whitespace": 2,
"no-iterator": 2,
"no-label-var": 2,
"no-lone-blocks": 2,
"no-lonely-if": 0,
"no-loop-func": 2,
"no-mixed-requires": [1, false],
"no-mixed-spaces-and-tabs": [2, false],
"linebreak-style": [1, "unix"],
"no-multi-spaces": 2,
"no-multi-str": 2,
"no-multiple-empty-lines": [1, {"max": 3}],
"no-native-reassign": 2,
"no-negated-in-lhs": 2,
"no-nested-ternary": 0,
"no-new": 2,
"no-new-func": 2,
"no-new-object": 2,
"no-new-require": 0,
"no-new-wrappers": 2,
"no-obj-calls": 2,
"no-octal": 2,
"no-octal-escape": 2,
"no-param-reassign": 0,
"no-path-concat": 0,
"no-plusplus": 0,
"no-process-env": 0,
"no-process-exit": 2,
"no-proto": 2,
"no-redeclare": 2,
"no-regex-spaces": 2,
"no-reserved-keys": 0,
"no-restricted-modules": 0,
"no-return-assign": 2,
"no-script-url": 2,
"no-self-compare": 0,
"no-sequences": 2,
"no-shadow": 2,
"no-shadow-restricted-names": 2,
"no-spaced-func": 2,
"no-sparse-arrays": 2,
"no-sync": 0,
"no-ternary": 0,
"no-trailing-spaces": 2,
"no-throw-literal": 1,
"no-undef": 2,
"no-undef-init": 2,
"no-undefined": 0,
"no-unneeded-ternary": 2,
"no-unreachable": 2,
"no-unused-expressions": 2,
"no-unused-vars": [2, {"vars": "all", "args": "after-used"}],
"no-use-before-define": 2,
"no-void": 0,
"no-var": 0,
"prefer-const": 0,
"no-warning-comments": [0, { "terms": ["todo", "fixme", "xxx"], "location": "start" }],
"no-with": 2,
"accessor-pairs": 0,
"block-scoped-var": 0,
"brace-style": [0, "1tbs"],
"camelcase": 1,
"comma-spacing": 2,
"comma-style": 0,
"complexity": [1, 11],
"computed-property-spacing": [0, "never"],
"consistent-return": 1,
"consistent-this": [0, "that"],
"curly": [2, "all"],
"default-case": 0,
"dot-location": 0,
"dot-notation": [2, { "allowKeywords": true }],
"eol-last": 0,
"eqeqeq": 2,
"func-names": 0,
"func-style": [1, "declaration"],
"generator-star": 0,
"generator-star-spacing": 0,
"guard-for-in": 1,
"handle-callback-err": 2,
"indent": 0,
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
"lines-around-comment": 0,
"max-depth": [2, 4],
"max-len": [2, 320, 4],
"max-nested-callbacks": [2, 2],
"max-params": [1, 8],
"max-statements": [1, 20],
"new-cap": 0,
"new-parens": 2,
"newline-after-var": 0,
"object-curly-spacing": [0, "never"],
"object-shorthand": 0,
"one-var": 0,
"operator-assignment": [0, "always"],
"operator-linebreak": 0,
"padded-blocks": 0,
"quote-props": 0,
"quotes": [2, "single"],
"radix": 0,
"semi": 2,
"semi-spacing": [2, {"before": false, "after": true}],
"sort-vars": 0,
"space-after-function-name": [0, "never"],
"space-after-keywords": [0, "always"],
"space-before-blocks": [0, "always"],
"space-before-function-paren": [0, "always"],
"space-before-function-parentheses": [0, "always"],
"space-in-brackets": [0, "never"],
"space-in-parens": [0, "never"],
"space-infix-ops": 2,
"keyword-spacing": 1,
"space-unary-ops": [2, { "words": true, "nonwords": false }],
"spaced-comment": 0,
"spaced-line-comment": [0, "always"],
"strict": [2, "function"],
"use-isnan": 2,
"valid-jsdoc": 0,
"valid-typeof": 2,
"vars-on-top": 0,
"wrap-iife": 0,
"wrap-regex": 0,
"yoda": [2, "never"]
},
"globals": {
"Konva" : false,
"define": false,
"test": false,
"assert": false,
"addStage": false,
"suite": false
}
}

Sorry, the diff of this file is not supported yet

#!/usr/bin/env bash
set -e
old_version="$(git describe --abbrev=0 --tags)"
new_version=$1
old_cdn="https://cdn.rawgit.com/konvajs/konva/${old_version}/konva.js"
new_cdn="https://cdn.rawgit.com/konvajs/konva/${new_version}/konva.js"
old_cdn_min="https://cdn.rawgit.com/konvajs/konva/${old_version}/konva.min.js"
new_cdn_min="https://cdn.rawgit.com/konvajs/konva/${new_version}/konva.min.js"
# make sure new version parameter is passed
if [ -z "$1" ]
then
echo "ERROR - pass new version. usage release.sh 0.11.0"
exit 2
fi
# make sure changle log updated
while true; do
read -p "Did you update CHANGELOG.md? " yn
case $yn in
[Yy]* ) break;;
[Nn]* ) exit;;
* ) echo "Please answer yes or no.";;
esac
done
echo "lint and test"
npm start lint test
echo "commit change log updates"
git commit -am "update CHANGELOG with new version" --allow-empty
echo "npm version $1 --no-git-tag-version"
npm version $1 --no-git-tag-version
echo "build for $1"
npm start build
git commit -am "build for $1" --allow-empty
echo "update CDN link in REAME"
perl -i -pe "s|${old_cdn_min}|${new_cdn_min}|g" ./README.md
git commit -am "update cdn link" --allow-empty
echo "create new git tag"
git tag $1
echo "generate documentation"
npm start api
echo "archive documentation"
zip -r konva-v${new_version}-documentation.zip ./api/*
rm -r ./api
echo "documentation is generated"
echo "include konva-v${new_version}-documentation.zip into version in github"
echo "copy konva.js into konva-site"
cp ./konva.js ../konva-site/
cd ../konva-site
echo "replace CDN links"
find source themes -exec perl -i -pe "s|${old_cdn}|${new_cdn}|g" {} +
find source themes -exec perl -i -pe "s|${old_cdn_min}|${new_cdn_min}|g" {} +
echo "regenerate site"
npm start
echo "commit site changes"
git add .
DATE=`date +%Y-%m-%d`
git commit -am "update ${DATE}" --allow-empty
git push
echo "clean previous generated site"
cd ../konvajs.github.io
rm -r ./*
echo "copy new generated files"
cp -r ../konva-site/public/* ./
git status
echo "commit and push site changes"
git add .
git commit -am "update ${DATE}"
git push
cd ../konva
git push
git push --tags
npm publish
echo "DONE!"
echo "-------"
echo "Now you need:"
echo "create new relese in github and attach documentation https://github.com/konvajs/konva/releases"
echo "update CDN link to ${new_cdn} at http://codepen.io/lavrton/pen/myBPGo"
(function() {
'use strict';
/**
* Arrow constructor
* @constructor
* @memberof Konva
* @augments Konva.Shape
* @param {Object} config
* @param {Array} config.points
* @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
* @param {Number} config.pointerWidth
* @@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
* });
*/
Konva.Arrow = function(config) {
this.____init(config);
};
Konva.Arrow.prototype = {
____init: function(config) {
// call super constructor
Konva.Line.call(this, config);
this.className = 'Arrow';
},
_sceneFunc: function(ctx) {
Konva.Line.prototype._sceneFunc.apply(this, arguments);
var PI2 = Math.PI * 2;
var points = this.points();
var n = points.length;
var dx = points[n - 2] - points[n - 4];
var dy = points[n - 1] - points[n - 3];
var radians = (Math.atan2(dy, dx) + PI2) % PI2;
var length = this.pointerLength();
var width = this.pointerWidth();
ctx.save();
ctx.beginPath();
ctx.translate(points[n - 2], points[n - 1]);
ctx.rotate(radians);
ctx.moveTo(0, 0);
ctx.lineTo(-length, width / 2);
ctx.lineTo(-length, -width / 2);
ctx.closePath();
ctx.restore();
if (this.pointerAtBeginning()) {
ctx.save();
ctx.translate(points[0], points[1]);
dx = points[2] - points[0];
dy = points[3] - points[1];
ctx.rotate((Math.atan2(-dy, -dx) + PI2) % PI2);
ctx.moveTo(0, 0);
ctx.lineTo(-length, width / 2);
ctx.lineTo(-length, -width / 2);
ctx.closePath();
ctx.restore();
}
ctx.fillStrokeShape(this);
}
};
Konva.Util.extend(Konva.Arrow, Konva.Line);
/**
* get/set pointerLength
* @name pointerLength
* @method
* @memberof Konva.Arrow.prototype
* @param {Number} Length of pointer of arrow.
* The default is 10.
* @returns {Number}
* @example
* // get tension
* var pointerLength = line.pointerLength();
*
* // set tension
* line.pointerLength(15);
*/
Konva.Factory.addGetterSetter(Konva.Arrow, 'pointerLength', 10);
/**
* get/set pointerWidth
* @name pointerWidth
* @method
* @memberof Konva.Arrow.prototype
* @param {Number} Width of pointer of arrow.
* The default is 10.
* @returns {Number}
* @example
* // get tension
* var pointerWidth = line.pointerWidth();
*
* // set tension
* line.pointerWidth(15);
*/
Konva.Factory.addGetterSetter(Konva.Arrow, 'pointerWidth', 10);
/**
* get/set pointerAtBeginning
* @name pointerAtBeginning
* @method
* @memberof Konva.Arrow.prototype
* @param {Number} Should pointer displayed at beginning of arrow.
* The default is false.
* @returns {Boolean}
* @example
* // get tension
* var pointerAtBeginning = line.pointerAtBeginning();
*
* // set tension
* line.pointerAtBeginning(true);
*/
Konva.Factory.addGetterSetter(Konva.Arrow, 'pointerAtBeginning', false);
Konva.Collection.mapMethods(Konva.Arrow);
})();
(function() {
'use strict';
// constants
var ATTR_CHANGE_LIST = ['fontFamily', 'fontSize', 'fontStyle', 'padding', 'lineHeight', 'text'],
CHANGE_KONVA = 'Change.konva',
NONE = 'none',
UP = 'up',
RIGHT = 'right',
DOWN = 'down',
LEFT = 'left',
LABEL = 'Label',
// cached variables
attrChangeListLen = ATTR_CHANGE_LIST.length;
/**
* Label constructor.&nbsp; Labels are groups that contain a Text and Tag shape
* @constructor
* @memberof Konva
* @param {Object} config
* @@nodeParams
* @example
* // create label
* var label = new Konva.Label({
* x: 100,
* y: 100,
* draggable: true
* });
*
* // add a tag to the label
* label.add(new Konva.Tag({
* fill: '#bbb',
* stroke: '#333',
* shadowColor: 'black',
* shadowBlur: 10,
* shadowOffset: [10, 10],
* shadowOpacity: 0.2,
* lineJoin: 'round',
* pointerDirection: 'up',
* pointerWidth: 20,
* pointerHeight: 20,
* cornerRadius: 5
* }));
*
* // add text to the label
* label.add(new Konva.Text({
* text: 'Hello World!',
* fontSize: 50,
* lineHeight: 1.2,
* padding: 10,
* fill: 'green'
* }));
*/
Konva.Label = function(config) {
this.____init(config);
};
Konva.Label.prototype = {
____init: function(config) {
var that = this;
Konva.Group.call(this, config);
this.className = LABEL;
this.on('add.konva', function(evt) {
that._addListeners(evt.child);
that._sync();
});
},
/**
* get Text shape for the label. You need to access the Text shape in order to update
* the text properties
* @name getText
* @method
* @memberof Konva.Label.prototype
*/
getText: function() {
return this.find('Text')[0];
},
/**
* get Tag shape for the label. You need to access the Tag shape in order to update
* the pointer properties and the corner radius
* @name getTag
* @method
* @memberof Konva.Label.prototype
*/
getTag: function() {
return this.find('Tag')[0];
},
_addListeners: function(text) {
var that = this,
n;
var func = function(){
that._sync();
};
// update text data for certain attr changes
for(n = 0; n < attrChangeListLen; n++) {
text.on(ATTR_CHANGE_LIST[n] + CHANGE_KONVA, func);
}
},
getWidth: function() {
return this.getText().getWidth();
},
getHeight: function() {
return this.getText().getHeight();
},
_sync: function() {
var text = this.getText(),
tag = this.getTag(),
width, height, pointerDirection, pointerWidth, x, y, pointerHeight;
if (text && tag) {
width = text.getWidth();
height = text.getHeight();
pointerDirection = tag.getPointerDirection();
pointerWidth = tag.getPointerWidth();
pointerHeight = tag.getPointerHeight();
x = 0;
y = 0;
switch(pointerDirection) {
case UP:
x = width / 2;
y = -1 * pointerHeight;
break;
case RIGHT:
x = width + pointerWidth;
y = height / 2;
break;
case DOWN:
x = width / 2;
y = height + pointerHeight;
break;
case LEFT:
x = -1 * pointerWidth;
y = height / 2;
break;
}
tag.setAttrs({
x: -1 * x,
y: -1 * y,
width: width,
height: height
});
text.setAttrs({
x: -1 * x,
y: -1 * y
});
}
}
};
Konva.Util.extend(Konva.Label, Konva.Group);
Konva.Collection.mapMethods(Konva.Label);
/**
* Tag constructor.&nbsp; A Tag can be configured
* to have a pointer element that points up, right, down, or left
* @constructor
* @memberof Konva
* @param {Object} config
* @param {String} [config.pointerDirection] can be up, right, down, left, or none; the default
* is none. When a pointer is present, the positioning of the label is relative to the tip of the pointer.
* @param {Number} [config.pointerWidth]
* @param {Number} [config.pointerHeight]
* @param {Number} [config.cornerRadius]
*/
Konva.Tag = function(config) {
this.___init(config);
};
Konva.Tag.prototype = {
___init: function(config) {
Konva.Shape.call(this, config);
this.className = 'Tag';
this.sceneFunc(this._sceneFunc);
},
_sceneFunc: function(context) {
var width = this.getWidth(),
height = this.getHeight(),
pointerDirection = this.getPointerDirection(),
pointerWidth = this.getPointerWidth(),
pointerHeight = this.getPointerHeight(),
cornerRadius = Math.min(this.getCornerRadius(), width / 2, height / 2);
context.beginPath();
if (!cornerRadius) {
context.moveTo(0, 0);
} else {
context.moveTo(cornerRadius, 0);
}
if (pointerDirection === UP) {
context.lineTo((width - pointerWidth) / 2, 0);
context.lineTo(width / 2, -1 * pointerHeight);
context.lineTo((width + pointerWidth) / 2, 0);
}
if(!cornerRadius) {
context.lineTo(width, 0);
} else {
context.lineTo(width - cornerRadius, 0);
context.arc(width - cornerRadius, cornerRadius, cornerRadius, Math.PI * 3 / 2, 0, false);
}
if (pointerDirection === RIGHT) {
context.lineTo(width, (height - pointerHeight) / 2);
context.lineTo(width + pointerWidth, height / 2);
context.lineTo(width, (height + pointerHeight) / 2);
}
if(!cornerRadius) {
context.lineTo(width, height);
} else {
context.lineTo(width, height - cornerRadius);
context.arc(width - cornerRadius, height - cornerRadius, cornerRadius, 0, Math.PI / 2, false);
}
if (pointerDirection === DOWN) {
context.lineTo((width + pointerWidth) / 2, height);
context.lineTo(width / 2, height + pointerHeight);
context.lineTo((width - pointerWidth) / 2, height);
}
if(!cornerRadius) {
context.lineTo(0, height);
} else {
context.lineTo(cornerRadius, height);
context.arc(cornerRadius, height - cornerRadius, cornerRadius, Math.PI / 2, Math.PI, false);
}
if (pointerDirection === LEFT) {
context.lineTo(0, (height + pointerHeight) / 2);
context.lineTo(-1 * pointerWidth, height / 2);
context.lineTo(0, (height - pointerHeight) / 2);
}
if(cornerRadius) {
context.lineTo(0, cornerRadius);
context.arc(cornerRadius, cornerRadius, cornerRadius, Math.PI, Math.PI * 3 / 2, false);
}
context.closePath();
context.fillStrokeShape(this);
},
getSelfRect: function() {
var x = 0,
y = 0,
pointerWidth = this.getPointerWidth(),
pointerHeight = this.getPointerHeight(),
direction = this.pointerDirection(),
width = this.getWidth(),
height = this.getHeight();
if (direction === UP) {
y -= pointerHeight;
height += pointerHeight;
} else if (direction === DOWN) {
height += pointerHeight;
} else if (direction === LEFT) {
// ARGH!!! I have no idea why should I used magic 1.5!!!!!!!!!
x -= pointerWidth * 1.5;
width += pointerWidth;
} else if (direction === RIGHT) {
width += pointerWidth * 1.5;
}
return {
x: x,
y: y,
width: width,
height: height
};
}
};
Konva.Util.extend(Konva.Tag, Konva.Shape);
Konva.Factory.addGetterSetter(Konva.Tag, 'pointerDirection', NONE);
/**
* set pointer Direction
* @name setPointerDirection
* @method
* @memberof Konva.Tag.prototype
* @param {String} pointerDirection can be up, right, down, left, or none. The
* default is none
*/
/**
* get pointer Direction
* @name getPointerDirection
* @method
* @memberof Konva.Tag.prototype
*/
Konva.Factory.addGetterSetter(Konva.Tag, 'pointerWidth', 0);
/**
* set pointer width
* @name setPointerWidth
* @method
* @memberof Konva.Tag.prototype
* @param {Number} pointerWidth
*/
/**
* get pointer width
* @name getPointerWidth
* @method
* @memberof Konva.Tag.prototype
*/
Konva.Factory.addGetterSetter(Konva.Tag, 'pointerHeight', 0);
/**
* set pointer height
* @name setPointerHeight
* @method
* @memberof Konva.Tag.prototype
* @param {Number} pointerHeight
*/
/**
* get pointer height
* @name getPointerHeight
* @method
* @memberof Konva.Tag.prototype
*/
Konva.Factory.addGetterSetter(Konva.Tag, 'cornerRadius', 0);
/**
* set corner radius
* @name setCornerRadius
* @method
* @memberof Konva.Tag.prototype
* @param {Number} corner radius
*/
/**
* get corner radius
* @name getCornerRadius
* @method
* @memberof Konva.Tag.prototype
*/
Konva.Collection.mapMethods(Konva.Tag);
})();
/*eslint-disable no-shadow, max-len, max-depth */
(function () {
'use strict';
/**
* Path constructor.
* @author Jason Follas
* @constructor
* @memberof Konva
* @augments Konva.Shape
* @param {Object} config
* @param {String} config.data SVG data string
* @@shapeParams
* @@nodeParams
* @example
* var path = new Konva.Path({
* x: 240,
* y: 40,
* data: 'M12.582,9.551C3.251,16.237,0.921,29.021,7.08,38.564l-2.36,1.689l4.893,2.262l4.893,2.262l-0.568-5.36l-0.567-5.359l-2.365,1.694c-4.657-7.375-2.83-17.185,4.352-22.33c7.451-5.338,17.817-3.625,23.156,3.824c5.337,7.449,3.625,17.813-3.821,23.152l2.857,3.988c9.617-6.893,11.827-20.277,4.935-29.896C35.591,4.87,22.204,2.658,12.582,9.551z',
* fill: 'green',
* scale: 2
* });
*/
Konva.Path = function (config) {
this.___init(config);
};
Konva.Path.prototype = {
___init: function (config) {
this.dataArray = [];
var that = this;
// call super constructor
Konva.Shape.call(this, config);
this.className = 'Path';
this.dataArray = Konva.Path.parsePathData(this.getData());
this.on('dataChange.konva', function () {
that.dataArray = Konva.Path.parsePathData(this.getData());
});
this.sceneFunc(this._sceneFunc);
},
_sceneFunc: function(context) {
var ca = this.dataArray,
closedPath = false;
// context position
context.beginPath();
for (var n = 0; n < ca.length; n++) {
var c = ca[n].command;
var p = ca[n].points;
switch (c) {
case 'L':
context.lineTo(p[0], p[1]);
break;
case 'M':
context.moveTo(p[0], p[1]);
break;
case 'C':
context.bezierCurveTo(p[0], p[1], p[2], p[3], p[4], p[5]);
break;
case 'Q':
context.quadraticCurveTo(p[0], p[1], p[2], p[3]);
break;
case 'A':
var cx = p[0], cy = p[1], rx = p[2], ry = p[3], theta = p[4], dTheta = p[5], psi = p[6], fs = p[7];
var r = (rx > ry) ? rx : ry;
var scaleX = (rx > ry) ? 1 : rx / ry;
var scaleY = (rx > ry) ? ry / rx : 1;
context.translate(cx, cy);
context.rotate(psi);
context.scale(scaleX, scaleY);
context.arc(0, 0, r, theta, theta + dTheta, 1 - fs);
context.scale(1 / scaleX, 1 / scaleY);
context.rotate(-psi);
context.translate(-cx, -cy);
break;
case 'z':
context.closePath();
closedPath = true;
break;
}
}
if (closedPath) {
context.fillStrokeShape(this);
}
else {
context.strokeShape(this);
}
},
getSelfRect: function() {
var points = [];
this.dataArray.forEach(function(data) {
points = points.concat(data.points);
});
var minX = points[0];
var maxX = points[0];
var minY = points[1];
var maxY = points[1];
var x, y;
for (var i = 0; i < points.length / 2; i++) {
x = points[i * 2]; y = points[i * 2 + 1];
minX = Math.min(minX, x);
maxX = Math.max(maxX, x);
minY = Math.min(minY, y);
maxY = Math.max(maxY, y);
}
return {
x: Math.round(minX),
y: Math.round(minY),
width: Math.round(maxX - minX),
height: Math.round(maxY - minY)
};
}
};
Konva.Util.extend(Konva.Path, Konva.Shape);
Konva.Path.getLineLength = function(x1, y1, x2, y2) {
return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
};
Konva.Path.getPointOnLine = function(dist, P1x, P1y, P2x, P2y, fromX, fromY) {
if(fromX === undefined) {
fromX = P1x;
}
if(fromY === undefined) {
fromY = P1y;
}
var m = (P2y - P1y) / ((P2x - P1x) + 0.00000001);
var run = Math.sqrt(dist * dist / (1 + m * m));
if(P2x < P1x) {
run *= -1;
}
var rise = m * run;
var pt;
if (P2x === P1x) { // vertical line
pt = {
x: fromX,
y: fromY + rise
};
} else if((fromY - P1y) / ((fromX - P1x) + 0.00000001) === m) {
pt = {
x: fromX + run,
y: fromY + rise
};
}
else {
var ix, iy;
var len = this.getLineLength(P1x, P1y, P2x, P2y);
if(len < 0.00000001) {
return undefined;
}
var u = (((fromX - P1x) * (P2x - P1x)) + ((fromY - P1y) * (P2y - P1y)));
u = u / (len * len);
ix = P1x + u * (P2x - P1x);
iy = P1y + u * (P2y - P1y);
var pRise = this.getLineLength(fromX, fromY, ix, iy);
var pRun = Math.sqrt(dist * dist - pRise * pRise);
run = Math.sqrt(pRun * pRun / (1 + m * m));
if(P2x < P1x) {
run *= -1;
}
rise = m * run;
pt = {
x: ix + run,
y: iy + rise
};
}
return pt;
};
Konva.Path.getPointOnCubicBezier = function(pct, P1x, P1y, P2x, P2y, P3x, P3y, P4x, P4y) {
function CB1(t) {
return t * t * t;
}
function CB2(t) {
return 3 * t * t * (1 - t);
}
function CB3(t) {
return 3 * t * (1 - t) * (1 - t);
}
function CB4(t) {
return (1 - t) * (1 - t) * (1 - t);
}
var x = P4x * CB1(pct) + P3x * CB2(pct) + P2x * CB3(pct) + P1x * CB4(pct);
var y = P4y * CB1(pct) + P3y * CB2(pct) + P2y * CB3(pct) + P1y * CB4(pct);
return {
x: x,
y: y
};
};
Konva.Path.getPointOnQuadraticBezier = function(pct, P1x, P1y, P2x, P2y, P3x, P3y) {
function QB1(t) {
return t * t;
}
function QB2(t) {
return 2 * t * (1 - t);
}
function QB3(t) {
return (1 - t) * (1 - t);
}
var x = P3x * QB1(pct) + P2x * QB2(pct) + P1x * QB3(pct);
var y = P3y * QB1(pct) + P2y * QB2(pct) + P1y * QB3(pct);
return {
x: x,
y: y
};
};
Konva.Path.getPointOnEllipticalArc = function(cx, cy, rx, ry, theta, psi) {
var cosPsi = Math.cos(psi), sinPsi = Math.sin(psi);
var pt = {
x: rx * Math.cos(theta),
y: ry * Math.sin(theta)
};
return {
x: cx + (pt.x * cosPsi - pt.y * sinPsi),
y: cy + (pt.x * sinPsi + pt.y * cosPsi)
};
};
/*
* get parsed data array from the data
* string. V, v, H, h, and l data are converted to
* L data for the purpose of high performance Path
* rendering
*/
Konva.Path.parsePathData = function(data) {
// Path Data Segment must begin with a moveTo
//m (x y)+ Relative moveTo (subsequent points are treated as lineTo)
//M (x y)+ Absolute moveTo (subsequent points are treated as lineTo)
//l (x y)+ Relative lineTo
//L (x y)+ Absolute LineTo
//h (x)+ Relative horizontal lineTo
//H (x)+ Absolute horizontal lineTo
//v (y)+ Relative vertical lineTo
//V (y)+ Absolute vertical lineTo
//z (closepath)
//Z (closepath)
//c (x1 y1 x2 y2 x y)+ Relative Bezier curve
//C (x1 y1 x2 y2 x y)+ Absolute Bezier curve
//q (x1 y1 x y)+ Relative Quadratic Bezier
//Q (x1 y1 x y)+ Absolute Quadratic Bezier
//t (x y)+ Shorthand/Smooth Relative Quadratic Bezier
//T (x y)+ Shorthand/Smooth Absolute Quadratic Bezier
//s (x2 y2 x y)+ Shorthand/Smooth Relative Bezier curve
//S (x2 y2 x y)+ Shorthand/Smooth Absolute Bezier curve
//a (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+ Relative Elliptical Arc
//A (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+ Absolute Elliptical Arc
// return early if data is not defined
if(!data) {
return [];
}
// command string
var cs = data;
// command chars
var cc = ['m', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z', 'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A'];
// convert white spaces to commas
cs = cs.replace(new RegExp(' ', 'g'), ',');
// create pipes so that we can split the data
for(var n = 0; n < cc.length; n++) {
cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
}
// create array
var arr = cs.split('|');
var ca = [];
// init context point
var cpx = 0;
var cpy = 0;
for( n = 1; n < arr.length; n++) {
var str = arr[n];
var c = str.charAt(0);
str = str.slice(1);
// remove ,- for consistency
str = str.replace(new RegExp(',-', 'g'), '-');
// add commas so that it's easy to split
str = str.replace(new RegExp('-', 'g'), ',-');
str = str.replace(new RegExp('e,-', 'g'), 'e-');
var p = str.split(',');
if(p.length > 0 && p[0] === '') {
p.shift();
}
// convert strings to floats
for(var i = 0; i < p.length; i++) {
p[i] = parseFloat(p[i]);
}
while(p.length > 0) {
if(isNaN(p[0])) {// case for a trailing comma before next command
break;
}
var cmd = null;
var points = [];
var startX = cpx, startY = cpy;
// Move var from within the switch to up here (jshint)
var prevCmd, ctlPtx, ctlPty; // Ss, Tt
var rx, ry, psi, fa, fs, x1, y1; // Aa
// convert l, H, h, V, and v to L
switch (c) {
// Note: Keep the lineTo's above the moveTo's in this switch
case 'l':
cpx += p.shift();
cpy += p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'L':
cpx = p.shift();
cpy = p.shift();
points.push(cpx, cpy);
break;
// Note: lineTo handlers need to be above this point
case 'm':
var dx = p.shift();
var dy = p.shift();
cpx += dx;
cpy += dy;
cmd = 'M';
// After closing the path move the current position
// to the the first point of the path (if any).
if(ca.length > 2 && ca[ca.length - 1].command === 'z'){
for(var idx = ca.length - 2; idx >= 0; idx--){
if(ca[idx].command === 'M'){
cpx = ca[idx].points[0] + dx;
cpy = ca[idx].points[1] + dy;
break;
}
}
}
points.push(cpx, cpy);
c = 'l';
// subsequent points are treated as relative lineTo
break;
case 'M':
cpx = p.shift();
cpy = p.shift();
cmd = 'M';
points.push(cpx, cpy);
c = 'L';
// subsequent points are treated as absolute lineTo
break;
case 'h':
cpx += p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'H':
cpx = p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'v':
cpy += p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'V':
cpy = p.shift();
cmd = 'L';
points.push(cpx, cpy);
break;
case 'C':
points.push(p.shift(), p.shift(), p.shift(), p.shift());
cpx = p.shift();
cpy = p.shift();
points.push(cpx, cpy);
break;
case 'c':
points.push(cpx + p.shift(), cpy + p.shift(), cpx + p.shift(), cpy + p.shift());
cpx += p.shift();
cpy += p.shift();
cmd = 'C';
points.push(cpx, cpy);
break;
case 'S':
ctlPtx = cpx;
ctlPty = cpy;
prevCmd = ca[ca.length - 1];
if(prevCmd.command === 'C') {
ctlPtx = cpx + (cpx - prevCmd.points[2]);
ctlPty = cpy + (cpy - prevCmd.points[3]);
}
points.push(ctlPtx, ctlPty, p.shift(), p.shift());
cpx = p.shift();
cpy = p.shift();
cmd = 'C';
points.push(cpx, cpy);
break;
case 's':
ctlPtx = cpx;
ctlPty = cpy;
prevCmd = ca[ca.length - 1];
if(prevCmd.command === 'C') {
ctlPtx = cpx + (cpx - prevCmd.points[2]);
ctlPty = cpy + (cpy - prevCmd.points[3]);
}
points.push(ctlPtx, ctlPty, cpx + p.shift(), cpy + p.shift());
cpx += p.shift();
cpy += p.shift();
cmd = 'C';
points.push(cpx, cpy);
break;
case 'Q':
points.push(p.shift(), p.shift());
cpx = p.shift();
cpy = p.shift();
points.push(cpx, cpy);
break;
case 'q':
points.push(cpx + p.shift(), cpy + p.shift());
cpx += p.shift();
cpy += p.shift();
cmd = 'Q';
points.push(cpx, cpy);
break;
case 'T':
ctlPtx = cpx;
ctlPty = cpy;
prevCmd = ca[ca.length - 1];
if(prevCmd.command === 'Q') {
ctlPtx = cpx + (cpx - prevCmd.points[0]);
ctlPty = cpy + (cpy - prevCmd.points[1]);
}
cpx = p.shift();
cpy = p.shift();
cmd = 'Q';
points.push(ctlPtx, ctlPty, cpx, cpy);
break;
case 't':
ctlPtx = cpx;
ctlPty = cpy;
prevCmd = ca[ca.length - 1];
if(prevCmd.command === 'Q') {
ctlPtx = cpx + (cpx - prevCmd.points[0]);
ctlPty = cpy + (cpy - prevCmd.points[1]);
}
cpx += p.shift();
cpy += p.shift();
cmd = 'Q';
points.push(ctlPtx, ctlPty, cpx, cpy);
break;
case 'A':
rx = p.shift();
ry = p.shift();
psi = p.shift();
fa = p.shift();
fs = p.shift();
x1 = cpx;
y1 = cpy;
cpx = p.shift();
cpy = p.shift();
cmd = 'A';
points = this.convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
break;
case 'a':
rx = p.shift();
ry = p.shift();
psi = p.shift();
fa = p.shift();
fs = p.shift();
x1 = cpx;
y1 = cpy; cpx += p.shift();
cpy += p.shift();
cmd = 'A';
points = this.convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
break;
}
ca.push({
command: cmd || c,
points: points,
start: {
x: startX,
y: startY
},
pathLength: this.calcLength(startX, startY, cmd || c, points)
});
}
if(c === 'z' || c === 'Z') {
ca.push({
command: 'z',
points: [],
start: undefined,
pathLength: 0
});
}
}
return ca;
};
Konva.Path.calcLength = function(x, y, cmd, points) {
var len, p1, p2, t;
var path = Konva.Path;
switch (cmd) {
case 'L':
return path.getLineLength(x, y, points[0], points[1]);
case 'C':
// Approximates by breaking curve into 100 line segments
len = 0.0;
p1 = path.getPointOnCubicBezier(0, x, y, points[0], points[1], points[2], points[3], points[4], points[5]);
for( t = 0.01; t <= 1; t += 0.01) {
p2 = path.getPointOnCubicBezier(t, x, y, points[0], points[1], points[2], points[3], points[4], points[5]);
len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
return len;
case 'Q':
// Approximates by breaking curve into 100 line segments
len = 0.0;
p1 = path.getPointOnQuadraticBezier(0, x, y, points[0], points[1], points[2], points[3]);
for( t = 0.01; t <= 1; t += 0.01) {
p2 = path.getPointOnQuadraticBezier(t, x, y, points[0], points[1], points[2], points[3]);
len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
return len;
case 'A':
// Approximates by breaking curve into line segments
len = 0.0;
var start = points[4];
// 4 = theta
var dTheta = points[5];
// 5 = dTheta
var end = points[4] + dTheta;
var inc = Math.PI / 180.0;
// 1 degree resolution
if(Math.abs(start - end) < inc) {
inc = Math.abs(start - end);
}
// Note: for purpose of calculating arc length, not going to worry about rotating X-axis by angle psi
p1 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], start, 0);
if(dTheta < 0) {// clockwise
for( t = start - inc; t > end; t -= inc) {
p2 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
}
else {// counter-clockwise
for( t = start + inc; t < end; t += inc) {
p2 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
}
p2 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], end, 0);
len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
return len;
}
return 0;
};
Konva.Path.convertEndpointToCenterParameterization = function(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg) {
// Derived from: http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
var psi = psiDeg * (Math.PI / 180.0);
var xp = Math.cos(psi) * (x1 - x2) / 2.0 + Math.sin(psi) * (y1 - y2) / 2.0;
var yp = -1 * Math.sin(psi) * (x1 - x2) / 2.0 + Math.cos(psi) * (y1 - y2) / 2.0;
var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);
if(lambda > 1) {
rx *= Math.sqrt(lambda);
ry *= Math.sqrt(lambda);
}
var f = Math.sqrt((((rx * rx) * (ry * ry)) - ((rx * rx) * (yp * yp)) - ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp) + (ry * ry) * (xp * xp)));
if(fa === fs) {
f *= -1;
}
if(isNaN(f)) {
f = 0;
}
var cxp = f * rx * yp / ry;
var cyp = f * -ry * xp / rx;
var cx = (x1 + x2) / 2.0 + Math.cos(psi) * cxp - Math.sin(psi) * cyp;
var cy = (y1 + y2) / 2.0 + Math.sin(psi) * cxp + Math.cos(psi) * cyp;
var vMag = function(v) {
return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
};
var vRatio = function(u, v) {
return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
};
var vAngle = function(u, v) {
return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) * Math.acos(vRatio(u, v));
};
var theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]);
var u = [(xp - cxp) / rx, (yp - cyp) / ry];
var v = [(-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry];
var dTheta = vAngle(u, v);
if(vRatio(u, v) <= -1) {
dTheta = Math.PI;
}
if(vRatio(u, v) >= 1) {
dTheta = 0;
}
if(fs === 0 && dTheta > 0) {
dTheta = dTheta - 2 * Math.PI;
}
if(fs === 1 && dTheta < 0) {
dTheta = dTheta + 2 * Math.PI;
}
return [cx, cy, rx, ry, theta, dTheta, psi, fs];
};
// add getters setters
Konva.Factory.addGetterSetter(Konva.Path, 'data');
/**
* set SVG path data string. This method
* also automatically parses the data string
* into a data array. Currently supported SVG data:
* M, m, L, l, H, h, V, v, Q, q, T, t, C, c, S, s, A, a, Z, z
* @name setData
* @method
* @memberof Konva.Path.prototype
* @param {String} SVG path command string
*/
/**
* get SVG path data string
* @name getData
* @method
* @memberof Konva.Path.prototype
*/
Konva.Collection.mapMethods(Konva.Path);
})();
(function() {
'use strict';
/**
* RegularPolygon constructor.&nbsp; Examples include triangles, squares, pentagons, hexagons, etc.
* @constructor
* @memberof Konva
* @augments Konva.Shape
* @param {Object} config
* @param {Number} config.sides
* @param {Number} config.radius
* @@shapeParams
* @@nodeParams
* @example
* var hexagon = new Konva.RegularPolygon({
* x: 100,
* y: 200,
* sides: 6,
* radius: 70,
* fill: 'red',
* stroke: 'black',
* strokeWidth: 4
* });
*/
Konva.RegularPolygon = function(config) {
this.___init(config);
};
Konva.RegularPolygon.prototype = {
_centroid: true,
___init: function(config) {
// call super constructor
Konva.Shape.call(this, config);
this.className = 'RegularPolygon';
this.sceneFunc(this._sceneFunc);
},
_sceneFunc: function(context) {
var sides = this.attrs.sides,
radius = this.attrs.radius,
n, x, y;
context.beginPath();
context.moveTo(0, 0 - radius);
for(n = 1; n < sides; n++) {
x = radius * Math.sin(n * 2 * Math.PI / sides);
y = -1 * radius * Math.cos(n * 2 * Math.PI / sides);
context.lineTo(x, y);
}
context.closePath();
context.fillStrokeShape(this);
},
getWidth: function() {
return this.getRadius() * 2;
},
// implements Shape.prototype.getHeight()
getHeight: function() {
return this.getRadius() * 2;
},
// implements Shape.prototype.setWidth()
setWidth: function(width) {
Konva.Node.prototype.setWidth.call(this, width);
if (this.radius() !== width / 2) {
this.setRadius(width / 2);
}
},
// implements Shape.prototype.setHeight()
setHeight: function(height) {
Konva.Node.prototype.setHeight.call(this, height);
if (this.radius() !== height / 2) {
this.setRadius(height / 2);
}
}
};
Konva.Util.extend(Konva.RegularPolygon, Konva.Shape);
// add getters setters
Konva.Factory.addGetterSetter(Konva.RegularPolygon, 'radius', 0);
/**
* set radius
* @name setRadius
* @method
* @memberof Konva.RegularPolygon.prototype
* @param {Number} radius
*/
/**
* get radius
* @name getRadius
* @method
* @memberof Konva.RegularPolygon.prototype
*/
Konva.Factory.addGetterSetter(Konva.RegularPolygon, 'sides', 0);
/**
* set number of sides
* @name setSides
* @method
* @memberof Konva.RegularPolygon.prototype
* @param {int} sides
*/
/**
* get number of sides
* @name getSides
* @method
* @memberof Konva.RegularPolygon.prototype
*/
Konva.Collection.mapMethods(Konva.RegularPolygon);
})();
(function() {
'use strict';
/**
* Star constructor
* @constructor
* @memberof Konva
* @augments Konva.Shape
* @param {Object} config
* @param {Integer} config.numPoints
* @param {Number} config.innerRadius
* @param {Number} config.outerRadius
* @@shapeParams
* @@nodeParams
* @example
* var star = new Konva.Star({
* x: 100,
* y: 200,
* numPoints: 5,
* innerRadius: 70,
* outerRadius: 70,
* fill: 'red',
* stroke: 'black',
* strokeWidth: 4
* });
*/
Konva.Star = function(config) {
this.___init(config);
};
Konva.Star.prototype = {
_centroid: true,
___init: function(config) {
// call super constructor
Konva.Shape.call(this, config);
this.className = 'Star';
this.sceneFunc(this._sceneFunc);
},
_sceneFunc: function(context) {
var innerRadius = this.innerRadius(),
outerRadius = this.outerRadius(),
numPoints = this.numPoints();
context.beginPath();
context.moveTo(0, 0 - outerRadius);
for(var n = 1; n < numPoints * 2; n++) {
var radius = n % 2 === 0 ? outerRadius : innerRadius;
var x = radius * Math.sin(n * Math.PI / numPoints);
var y = -1 * radius * Math.cos(n * Math.PI / numPoints);
context.lineTo(x, y);
}
context.closePath();
context.fillStrokeShape(this);
},
// implements Shape.prototype.getWidth()
getWidth: function() {
return this.getOuterRadius() * 2;
},
// implements Shape.prototype.getHeight()
getHeight: function() {
return this.getOuterRadius() * 2;
},
// implements Shape.prototype.setWidth()
setWidth: function(width) {
Konva.Node.prototype.setWidth.call(this, width);
if (this.outerRadius() !== width / 2) {
this.setOuterRadius(width / 2);
}
},
// implements Shape.prototype.setHeight()
setHeight: function(height) {
Konva.Node.prototype.setHeight.call(this, height);
if (this.outerRadius() !== height / 2) {
this.setOuterRadius(height / 2);
}
}
};
Konva.Util.extend(Konva.Star, Konva.Shape);
// add getters setters
Konva.Factory.addGetterSetter(Konva.Star, 'numPoints', 5);
/**
* set number of points
* @name setNumPoints
* @method
* @memberof Konva.Star.prototype
* @param {Integer} points
*/
/**
* get number of points
* @name getNumPoints
* @method
* @memberof Konva.Star.prototype
*/
Konva.Factory.addGetterSetter(Konva.Star, 'innerRadius', 0);
/**
* set inner radius
* @name setInnerRadius
* @method
* @memberof Konva.Star.prototype
* @param {Number} radius
*/
/**
* get inner radius
* @name getInnerRadius
* @method
* @memberof Konva.Star.prototype
*/
Konva.Factory.addGetterSetter(Konva.Star, 'outerRadius', 0);
/**
* set outer radius
* @name setOuterRadius
* @method
* @memberof Konva.Star.prototype
* @param {Number} radius
*/
/**
* get outer radius
* @name getOuterRadius
* @method
* @memberof Konva.Star.prototype
*/
Konva.Collection.mapMethods(Konva.Star);
})();
(function() {
'use strict';
var EMPTY_STRING = '',
//CALIBRI = 'Calibri',
NORMAL = 'normal';
/**
* Path constructor.
* @author Jason Follas
* @constructor
* @memberof Konva
* @augments Konva.Shape
* @param {Object} config
* @param {String} [config.fontFamily] default is Calibri
* @param {Number} [config.fontSize] default is 12
* @param {String} [config.fontStyle] can be normal, bold, or italic. Default is normal
* @param {String} [config.fontVariant] can be normal or small-caps. Default is normal
* @param {String} config.text
* @param {String} config.data SVG data string
* @@shapeParams
* @@nodeParams
* @example
* var textpath = new Konva.TextPath({
* x: 100,
* y: 50,
* fill: '#333',
* fontSize: '24',
* fontFamily: 'Arial',
* text: 'All the world\'s a stage, and all the men and women merely players.',
* data: 'M10,10 C0,0 10,150 100,100 S300,150 400,50'
* });
*/
Konva.TextPath = function(config) {
this.___init(config);
};
function _fillFunc(context) {
context.fillText(this.partialText, 0, 0);
}
function _strokeFunc(context) {
context.strokeText(this.partialText, 0, 0);
}
Konva.TextPath.prototype = {
___init: function(config) {
var that = this;
this.dummyCanvas = Konva.Util.createCanvasElement();
this.dataArray = [];
// call super constructor
Konva.Shape.call(this, config);
// overrides
// TODO: shouldn't this be on the prototype?
this._fillFunc = _fillFunc;
this._strokeFunc = _strokeFunc;
this._fillFuncHit = _fillFunc;
this._strokeFuncHit = _strokeFunc;
this.className = 'TextPath';
this.dataArray = Konva.Path.parsePathData(this.attrs.data);
this.on('dataChange.konva', function() {
that.dataArray = Konva.Path.parsePathData(this.attrs.data);
});
// update text data for certain attr changes
this.on('textChange.konva textStroke.konva textStrokeWidth.konva', that._setTextData);
that._setTextData();
this.sceneFunc(this._sceneFunc);
},
_sceneFunc: function(context) {
context.setAttr('font', this._getContextFont());
context.setAttr('textBaseline', 'middle');
context.setAttr('textAlign', 'left');
context.save();
var glyphInfo = this.glyphInfo;
for(var i = 0; i < glyphInfo.length; i++) {
context.save();
var p0 = glyphInfo[i].p0;
context.translate(p0.x, p0.y);
context.rotate(glyphInfo[i].rotation);
this.partialText = glyphInfo[i].text;
context.fillStrokeShape(this);
context.restore();
//// To assist with debugging visually, uncomment following
// context.beginPath();
// if (i % 2)
// context.strokeStyle = 'cyan';
// else
// context.strokeStyle = 'green';
// var p1 = glyphInfo[i].p1;
// context.moveTo(p0.x, p0.y);
// context.lineTo(p1.x, p1.y);
// context.stroke();
}
context.restore();
},
/**
* get text width in pixels
* @method
* @memberof Konva.TextPath.prototype
*/
getTextWidth: function() {
return this.textWidth;
},
/**
* get text height in pixels
* @method
* @memberof Konva.TextPath.prototype
*/
getTextHeight: function() {
return this.textHeight;
},
/**
* set text
* @method
* @memberof Konva.TextPath.prototype
* @param {String} text
*/
setText: function(text) {
Konva.Text.prototype.setText.call(this, text);
},
_getTextSize: function(text) {
var dummyCanvas = this.dummyCanvas;
var _context = dummyCanvas.getContext('2d');
_context.save();
_context.font = this._getContextFont();
var metrics = _context.measureText(text);
_context.restore();
return {
width: metrics.width,
height: parseInt(this.attrs.fontSize, 10)
};
},
_setTextData: function() {
var that = this;
var size = this._getTextSize(this.attrs.text);
this.textWidth = size.width;
this.textHeight = size.height;
this.glyphInfo = [];
var charArr = this.attrs.text.split('');
var p0, p1, pathCmd;
var pIndex = -1;
var currentT = 0;
var getNextPathSegment = function() {
currentT = 0;
var pathData = that.dataArray;
for(var j = pIndex + 1; j < pathData.length; j++) {
if(pathData[j].pathLength > 0) {
pIndex = j;
return pathData[j];
}
else if(pathData[j].command === 'M') {
p0 = {
x: pathData[j].points[0],
y: pathData[j].points[1]
};
}
}
return {};
};
var findSegmentToFitCharacter = function(c) {
var glyphWidth = that._getTextSize(c).width;
var currLen = 0;
var attempts = 0;
p1 = undefined;
while(Math.abs(glyphWidth - currLen) / glyphWidth > 0.01 && attempts < 25) {
attempts++;
var cumulativePathLength = currLen;
while(pathCmd === undefined) {
pathCmd = getNextPathSegment();
if(pathCmd && cumulativePathLength + pathCmd.pathLength < glyphWidth) {
cumulativePathLength += pathCmd.pathLength;
pathCmd = undefined;
}
}
if(pathCmd === {} || p0 === undefined) {
return undefined;
}
var needNewSegment = false;
switch (pathCmd.command) {
case 'L':
if(Konva.Path.getLineLength(p0.x, p0.y, pathCmd.points[0], pathCmd.points[1]) > glyphWidth) {
p1 = Konva.Path.getPointOnLine(glyphWidth, p0.x, p0.y, pathCmd.points[0], pathCmd.points[1], p0.x, p0.y);
}
else {
pathCmd = undefined;
}
break;
case 'A':
var start = pathCmd.points[4];
// 4 = theta
var dTheta = pathCmd.points[5];
// 5 = dTheta
var end = pathCmd.points[4] + dTheta;
if(currentT === 0){
currentT = start + 0.00000001;
}
// Just in case start is 0
else if(glyphWidth > currLen) {
currentT += (Math.PI / 180.0) * dTheta / Math.abs(dTheta);
}
else {
currentT -= Math.PI / 360.0 * dTheta / Math.abs(dTheta);
}
// Credit for bug fix: @therth https://github.com/ericdrowell/KonvaJS/issues/249
// Old code failed to render text along arc of this path: "M 50 50 a 150 50 0 0 1 250 50 l 50 0"
if(dTheta < 0 && currentT < end || dTheta >= 0 && currentT > end) {
currentT = end;
needNewSegment = true;
}
p1 = Konva.Path.getPointOnEllipticalArc(pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3], currentT, pathCmd.points[6]);
break;
case 'C':
if(currentT === 0) {
if(glyphWidth > pathCmd.pathLength) {
currentT = 0.00000001;
}
else {
currentT = glyphWidth / pathCmd.pathLength;
}
}
else if(glyphWidth > currLen) {
currentT += (glyphWidth - currLen) / pathCmd.pathLength;
}
else {
currentT -= (currLen - glyphWidth) / pathCmd.pathLength;
}
if(currentT > 1.0) {
currentT = 1.0;
needNewSegment = true;
}
p1 = Konva.Path.getPointOnCubicBezier(currentT, pathCmd.start.x, pathCmd.start.y, pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3], pathCmd.points[4], pathCmd.points[5]);
break;
case 'Q':
if(currentT === 0) {
currentT = glyphWidth / pathCmd.pathLength;
}
else if(glyphWidth > currLen) {
currentT += (glyphWidth - currLen) / pathCmd.pathLength;
}
else {
currentT -= (currLen - glyphWidth) / pathCmd.pathLength;
}
if(currentT > 1.0) {
currentT = 1.0;
needNewSegment = true;
}
p1 = Konva.Path.getPointOnQuadraticBezier(currentT, pathCmd.start.x, pathCmd.start.y, pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3]);
break;
}
if(p1 !== undefined) {
currLen = Konva.Path.getLineLength(p0.x, p0.y, p1.x, p1.y);
}
if(needNewSegment) {
needNewSegment = false;
pathCmd = undefined;
}
}
};
for(var i = 0; i < charArr.length; i++) {
// Find p1 such that line segment between p0 and p1 is approx. width of glyph
findSegmentToFitCharacter(charArr[i]);
if(p0 === undefined || p1 === undefined) {
break;
}
var width = Konva.Path.getLineLength(p0.x, p0.y, p1.x, p1.y);
// Note: Since glyphs are rendered one at a time, any kerning pair data built into the font will not be used.
// Can foresee having a rough pair table built in that the developer can override as needed.
var kern = 0;
// placeholder for future implementation
var midpoint = Konva.Path.getPointOnLine(kern + width / 2.0, p0.x, p0.y, p1.x, p1.y);
var rotation = Math.atan2((p1.y - p0.y), (p1.x - p0.x));
this.glyphInfo.push({
transposeX: midpoint.x,
transposeY: midpoint.y,
text: charArr[i],
rotation: rotation,
p0: p0,
p1: p1
});
p0 = p1;
}
},
getSelfRect: function() {
var points = [];
var fontSize = this.fontSize();
this.glyphInfo.forEach(function(info) {
points.push(info.p0.x);
points.push(info.p0.y);
points.push(info.p1.x);
points.push(info.p1.y);
});
var minX = points[0];
var maxX = points[0];
var minY = points[0];
var maxY = points[0];
var x, y;
for (var i = 0; i < points.length / 2; i++) {
x = points[i * 2]; y = points[i * 2 + 1];
minX = Math.min(minX, x);
maxX = Math.max(maxX, x);
minY = Math.min(minY, y);
maxY = Math.max(maxY, y);
}
return {
x: Math.round(minX) - fontSize,
y: Math.round(minY) - fontSize,
width: Math.round(maxX - minX) + fontSize * 2,
height: Math.round(maxY - minY) + fontSize * 2
};
}
};
// map TextPath methods to Text
Konva.TextPath.prototype._getContextFont = Konva.Text.prototype._getContextFont;
Konva.Util.extend(Konva.TextPath, Konva.Shape);
// add setters and getters
Konva.Factory.addGetterSetter(Konva.TextPath, 'fontFamily', 'Arial');
/**
* set font family
* @name setFontFamily
* @method
* @memberof Konva.TextPath.prototype
* @param {String} fontFamily
*/
/**
* get font family
* @name getFontFamily
* @method
* @memberof Konva.TextPath.prototype
*/
Konva.Factory.addGetterSetter(Konva.TextPath, 'fontSize', 12);
/**
* set font size
* @name setFontSize
* @method
* @memberof Konva.TextPath.prototype
* @param {int} fontSize
*/
/**
* get font size
* @name getFontSize
* @method
* @memberof Konva.TextPath.prototype
*/
Konva.Factory.addGetterSetter(Konva.TextPath, 'fontStyle', NORMAL);
/**
* set font style. Can be 'normal', 'italic', or 'bold'. 'normal' is the default.
* @name setFontStyle
* @method
* @memberof Konva.TextPath.prototype
* @param {String} fontStyle
*/
/**
* get font style
* @name getFontStyle
* @method
* @memberof Konva.TextPath.prototype
*/
Konva.Factory.addGetterSetter(Konva.TextPath, 'fontVariant', NORMAL);
/**
* set font variant. Can be 'normal' or 'small-caps'. 'normal' is the default.
* @name setFontVariant
* @method
* @memberof Konva.TextPath.prototype
* @param {String} fontVariant
*/
/**
* @get font variant
* @name getFontVariant
* @method
* @memberof Konva.TextPath.prototype
*/
Konva.Factory.addGetter(Konva.TextPath, 'text', EMPTY_STRING);
/**
* get text
* @name getText
* @method
* @memberof Konva.TextPath.prototype
*/
Konva.Collection.mapMethods(Konva.TextPath);
})();

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display