mirror of
https://github.com/konvajs/konva.git
synced 2025-06-28 15:23:44 +08:00
commit
2bc0465186
@ -27,6 +27,12 @@ Kinetic.Path = function(config) {
|
|||||||
case 'M':
|
case 'M':
|
||||||
context.moveTo(p[0], p[1]);
|
context.moveTo(p[0], p[1]);
|
||||||
break;
|
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 'z':
|
case 'z':
|
||||||
context.closePath();
|
context.closePath();
|
||||||
break;
|
break;
|
||||||
@ -51,10 +57,36 @@ Kinetic.Path.prototype = {
|
|||||||
* rendering
|
* rendering
|
||||||
*/
|
*/
|
||||||
getCommandsArray: function() {
|
getCommandsArray: function() {
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// Note: SVG a,A not implemented here
|
||||||
|
//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
|
||||||
|
|
||||||
|
|
||||||
// command string
|
// command string
|
||||||
var cs = this.attrs.commands;
|
var cs = this.attrs.commands;
|
||||||
// command chars
|
// command chars
|
||||||
var cc = ['M', 'l', 'L', 'v', 'V', 'h', 'H', 'z'];
|
var cc = ['m', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z', 'c', 'C', 'q', 'Q', 't', 'T', 's', 'S'];
|
||||||
// convert white spaces to commas
|
// convert white spaces to commas
|
||||||
cs = cs.replace(new RegExp(' ', 'g'), ',');
|
cs = cs.replace(new RegExp(' ', 'g'), ',');
|
||||||
// create pipes so that we can split the commands
|
// create pipes so that we can split the commands
|
||||||
@ -83,44 +115,157 @@ Kinetic.Path.prototype = {
|
|||||||
for(var i = 0; i < p.length; i++) {
|
for(var i = 0; i < p.length; i++) {
|
||||||
p[i] = parseFloat(p[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 = undefined;
|
||||||
|
var points = [];
|
||||||
|
|
||||||
// convert l, H, h, V, and v to L
|
// convert l, H, h, V, and v to L
|
||||||
switch(c) {
|
switch(c) {
|
||||||
case 'M':
|
|
||||||
cpx = p[0];
|
// Note: Keep the lineTo's above the moveTo's in this switch
|
||||||
cpy = p[1];
|
|
||||||
break;
|
|
||||||
case 'l':
|
case 'l':
|
||||||
cpx += p[0];
|
cpx += p.shift();
|
||||||
cpy += p[1];
|
cpy += p.shift();
|
||||||
|
cmd = 'L';
|
||||||
|
points.push(cpx, cpy);
|
||||||
break;
|
break;
|
||||||
case 'L':
|
case 'L':
|
||||||
cpx = p[0];
|
cpx = p.shift();
|
||||||
cpy = p[1];
|
cpy = p.shift();
|
||||||
|
points.push(cpx, cpy);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// Note: lineTo handlers need to be above this point
|
||||||
|
case 'm':
|
||||||
|
cpx += p.shift();
|
||||||
|
cpy += p.shift();
|
||||||
|
cmd = 'M';
|
||||||
|
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':
|
case 'h':
|
||||||
cpx += p[0];
|
cpx += p.shift();
|
||||||
|
cmd = 'L';
|
||||||
|
points.push(cpx, cpy);
|
||||||
break;
|
break;
|
||||||
case 'H':
|
case 'H':
|
||||||
cpx = p[0];
|
cpx = p.shift();
|
||||||
|
cmd = 'L';
|
||||||
|
points.push(cpx, cpy);
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
cpy += p[0];
|
cpy += p.shift();
|
||||||
|
cmd = 'L';
|
||||||
|
points.push(cpx, cpy);
|
||||||
break;
|
break;
|
||||||
case 'V':
|
case 'V':
|
||||||
cpy = p[0];
|
cpy = p.shift();
|
||||||
|
cmd = 'L';
|
||||||
|
points.push(cpx, cpy);
|
||||||
break;
|
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':
|
||||||
|
var ctlPtx = cpx, ctlPty = cpy;
|
||||||
|
var prevCmd = ca[ca.length-1];
|
||||||
|
if (prevCmd.command === 'C') {
|
||||||
|
ctlPtx = cpx + (cpx - prevCmd.points[2]);
|
||||||
|
ctlPty = cpy + (cpy - prevCmd.points[3]);
|
||||||
}
|
}
|
||||||
// reassign command
|
points.push(ctlPtx, ctlPty, p.shift(), p.shift())
|
||||||
if(c == 'l' || c == 'V' || c == 'v' || c == 'H' || c == 'h') {
|
cpx = p.shift();
|
||||||
c = 'L';
|
cpy = p.shift();
|
||||||
p[0] = cpx;
|
cmd = 'C';
|
||||||
p[1] = cpy;
|
points.push(cpx, cpy);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
var ctlPtx = cpx, ctlPty = cpy;
|
||||||
|
var 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':
|
||||||
|
var ctlPtx = cpx, ctlPty = cpy;
|
||||||
|
var 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':
|
||||||
|
var ctlPtx = cpx, ctlPty = cpy;
|
||||||
|
var 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
ca.push({
|
ca.push({
|
||||||
command: c,
|
command: cmd || c,
|
||||||
points: p
|
points: points
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (c === 'z' || c === 'Z')
|
||||||
|
ca.push( {command: 'z', points: [] });
|
||||||
|
}
|
||||||
|
|
||||||
return ca;
|
return ca;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@ -133,7 +278,7 @@ Kinetic.Path.prototype = {
|
|||||||
* set SVG path commands string. This method
|
* set SVG path commands string. This method
|
||||||
* also automatically parses the commands string
|
* also automatically parses the commands string
|
||||||
* into a commands array. Currently supported SVG commands:
|
* into a commands array. Currently supported SVG commands:
|
||||||
* M, L, l, H, h, V, v, z
|
* M, m, L, l, H, h, V, v, Q, q, T, t, C, c, S, s, Z, z
|
||||||
* @param {String} SVG path command string
|
* @param {String} SVG path command string
|
||||||
*/
|
*/
|
||||||
setCommands: function(commands) {
|
setCommands: function(commands) {
|
||||||
|
@ -1227,6 +1227,50 @@ Test.prototype.tests = {
|
|||||||
path.setCommands('M200,100h100v50z');
|
path.setCommands('M200,100h100v50z');
|
||||||
|
|
||||||
},
|
},
|
||||||
|
'SHAPE - moveTo with implied lineTos and trailing comma': function(containerId) {
|
||||||
|
var stage = new Kinetic.Stage({
|
||||||
|
container: containerId,
|
||||||
|
width: 1024,
|
||||||
|
height: 480,
|
||||||
|
scale: 0.5,
|
||||||
|
x: 50,
|
||||||
|
y: 10
|
||||||
|
});
|
||||||
|
var layer = new Kinetic.Layer();
|
||||||
|
|
||||||
|
var path = new Kinetic.Path({
|
||||||
|
commands: 'm200,100,100,0,0,50,z',
|
||||||
|
fill: '#fcc',
|
||||||
|
stroke: '#333',
|
||||||
|
strokeWidth: 2,
|
||||||
|
shadow: {
|
||||||
|
color: 'maroon',
|
||||||
|
blur: 2,
|
||||||
|
offset: [10, 10],
|
||||||
|
alpha: 0.5
|
||||||
|
},
|
||||||
|
draggable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
path.on('mouseover', function() {
|
||||||
|
this.setFill('red');
|
||||||
|
layer.draw();
|
||||||
|
});
|
||||||
|
|
||||||
|
path.on('mouseout', function() {
|
||||||
|
this.setFill('#ccc');
|
||||||
|
layer.draw();
|
||||||
|
});
|
||||||
|
|
||||||
|
layer.add(path);
|
||||||
|
|
||||||
|
stage.add(layer);
|
||||||
|
|
||||||
|
test(path.getCommands() === 'm200,100,100,0,0,50,z', 'commands are incorrect');
|
||||||
|
test(path.getCommandsArray().length === 4, 'commands array should have 4 elements');
|
||||||
|
|
||||||
|
test(path.getCommandsArray()[1].command === 'L', 'second command should be an implied lineTo');
|
||||||
|
},
|
||||||
'SHAPE - add map path': function(containerId) {
|
'SHAPE - add map path': function(containerId) {
|
||||||
var stage = new Kinetic.Stage({
|
var stage = new Kinetic.Stage({
|
||||||
container: containerId,
|
container: containerId,
|
||||||
@ -1247,17 +1291,12 @@ Test.prototype.tests = {
|
|||||||
fill: '#ccc',
|
fill: '#ccc',
|
||||||
stroke: '#999',
|
stroke: '#999',
|
||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
/*
|
|
||||||
shadow: {
|
|
||||||
color: 'black',
|
|
||||||
blur: 2,
|
|
||||||
offset: [10, 10]
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (key === 'US')
|
||||||
|
test(path.getCommandsArray()[0].command === 'M', 'first command should be a moveTo');
|
||||||
|
|
||||||
path.on('mouseover', function() {
|
path.on('mouseover', function() {
|
||||||
//console.log(1)
|
|
||||||
this.setFill('red');
|
this.setFill('red');
|
||||||
mapLayer.draw();
|
mapLayer.draw();
|
||||||
});
|
});
|
||||||
@ -1272,6 +1311,181 @@ Test.prototype.tests = {
|
|||||||
|
|
||||||
stage.add(mapLayer);
|
stage.add(mapLayer);
|
||||||
|
|
||||||
|
},
|
||||||
|
'SHAPE - curved arrow path': function(containerId) {
|
||||||
|
var stage = new Kinetic.Stage({
|
||||||
|
container: containerId,
|
||||||
|
width: 1024,
|
||||||
|
height: 480,
|
||||||
|
throttle: 80,
|
||||||
|
scale: 1.5,
|
||||||
|
x: 50,
|
||||||
|
y: 10
|
||||||
|
});
|
||||||
|
var layer = new Kinetic.Layer();
|
||||||
|
|
||||||
|
var c = "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";
|
||||||
|
|
||||||
|
var path = new Kinetic.Path({
|
||||||
|
commands: c,
|
||||||
|
fill: '#ccc',
|
||||||
|
stroke: '#999',
|
||||||
|
strokeWidth: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
path.on('mouseover', function() {
|
||||||
|
this.setFill('red');
|
||||||
|
layer.draw();
|
||||||
|
});
|
||||||
|
|
||||||
|
path.on('mouseout', function() {
|
||||||
|
this.setFill('#ccc');
|
||||||
|
layer.draw();
|
||||||
|
});
|
||||||
|
|
||||||
|
layer.add(path);
|
||||||
|
stage.add(layer);
|
||||||
|
|
||||||
|
},
|
||||||
|
'SHAPE - Quadradic Curve test from SVG w3c spec': function(containerId) {
|
||||||
|
var stage = new Kinetic.Stage({
|
||||||
|
container: containerId,
|
||||||
|
width: 1024,
|
||||||
|
height: 480,
|
||||||
|
throttle: 80,
|
||||||
|
scale: 0.25,
|
||||||
|
x: 50,
|
||||||
|
y: 10
|
||||||
|
});
|
||||||
|
var layer = new Kinetic.Layer();
|
||||||
|
|
||||||
|
var c = "M200,300 Q400,50 600,300 T1000,300";
|
||||||
|
|
||||||
|
var path = new Kinetic.Path({
|
||||||
|
commands: c,
|
||||||
|
stroke: 'red',
|
||||||
|
strokeWidth: 5,
|
||||||
|
});
|
||||||
|
|
||||||
|
layer.add(path);
|
||||||
|
|
||||||
|
layer.add(new Kinetic.Circle({
|
||||||
|
x: 200,
|
||||||
|
y: 300,
|
||||||
|
radius: 10,
|
||||||
|
fill: 'black'
|
||||||
|
}));
|
||||||
|
|
||||||
|
layer.add(new Kinetic.Circle({
|
||||||
|
x: 600,
|
||||||
|
y: 300,
|
||||||
|
radius: 10,
|
||||||
|
fill: 'black'
|
||||||
|
}));
|
||||||
|
|
||||||
|
layer.add(new Kinetic.Circle({
|
||||||
|
x: 1000,
|
||||||
|
y: 300,
|
||||||
|
radius: 10,
|
||||||
|
fill: 'black'
|
||||||
|
}));
|
||||||
|
|
||||||
|
layer.add(new Kinetic.Circle({
|
||||||
|
x: 400,
|
||||||
|
y: 50,
|
||||||
|
radius: 10,
|
||||||
|
fill: '#888'
|
||||||
|
}));
|
||||||
|
|
||||||
|
layer.add(new Kinetic.Circle({
|
||||||
|
x: 800,
|
||||||
|
y: 550,
|
||||||
|
radius: 10,
|
||||||
|
fill: '#888'
|
||||||
|
}));
|
||||||
|
|
||||||
|
layer.add(new Kinetic.Path({
|
||||||
|
commands: "M200,300 L400,50L600,300L800,550L1000,300",
|
||||||
|
stroke: "#888",
|
||||||
|
strokeWidth: 2
|
||||||
|
}));
|
||||||
|
|
||||||
|
stage.add(layer);
|
||||||
|
|
||||||
|
},
|
||||||
|
'SHAPE - Cubic Bezier Curve test from SVG w3c spec': function(containerId) {
|
||||||
|
var stage = new Kinetic.Stage({
|
||||||
|
container: containerId,
|
||||||
|
width: 1024,
|
||||||
|
height: 480,
|
||||||
|
throttle: 80,
|
||||||
|
scale: 0.5,
|
||||||
|
x: 50,
|
||||||
|
y: 10
|
||||||
|
});
|
||||||
|
var layer = new Kinetic.Layer();
|
||||||
|
|
||||||
|
var c = "M100,200 C100,100 250,100 250,200 S400,300 400,200";
|
||||||
|
|
||||||
|
var path = new Kinetic.Path({
|
||||||
|
commands: c,
|
||||||
|
stroke: 'red',
|
||||||
|
strokeWidth: 5,
|
||||||
|
});
|
||||||
|
|
||||||
|
layer.add(path);
|
||||||
|
|
||||||
|
layer.add(new Kinetic.Circle({
|
||||||
|
x: 100,
|
||||||
|
y: 200,
|
||||||
|
radius: 10,
|
||||||
|
stroke: '#888'
|
||||||
|
}));
|
||||||
|
|
||||||
|
layer.add(new Kinetic.Circle({
|
||||||
|
x: 250,
|
||||||
|
y: 200,
|
||||||
|
radius: 10,
|
||||||
|
stroke: '#888'
|
||||||
|
}));
|
||||||
|
|
||||||
|
layer.add(new Kinetic.Circle({
|
||||||
|
x: 400,
|
||||||
|
y: 200,
|
||||||
|
radius: 10,
|
||||||
|
stroke: '#888'
|
||||||
|
}));
|
||||||
|
|
||||||
|
layer.add(new Kinetic.Circle({
|
||||||
|
x: 100,
|
||||||
|
y: 100,
|
||||||
|
radius: 10,
|
||||||
|
fill: '#888'
|
||||||
|
}));
|
||||||
|
|
||||||
|
layer.add(new Kinetic.Circle({
|
||||||
|
x: 250,
|
||||||
|
y: 100,
|
||||||
|
radius: 10,
|
||||||
|
fill: '#888'
|
||||||
|
}));
|
||||||
|
|
||||||
|
layer.add(new Kinetic.Circle({
|
||||||
|
x: 400,
|
||||||
|
y: 300,
|
||||||
|
radius: 10,
|
||||||
|
fill: '#888'
|
||||||
|
}));
|
||||||
|
|
||||||
|
layer.add(new Kinetic.Circle({
|
||||||
|
x: 250,
|
||||||
|
y: 300,
|
||||||
|
radius: 10,
|
||||||
|
stroke: 'blue'
|
||||||
|
}));
|
||||||
|
|
||||||
|
stage.add(layer);
|
||||||
|
|
||||||
},
|
},
|
||||||
'SHAPE - add shape with custom attr pointing to self': function(containerId) {
|
'SHAPE - add shape with custom attr pointing to self': function(containerId) {
|
||||||
var stage = new Kinetic.Stage({
|
var stage = new Kinetic.Stage({
|
||||||
|
Loading…
Reference in New Issue
Block a user