mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2024-12-29 09:22:22 -05:00
Implement non-scaling strokes through Style#strokeScaling.
Closes #418.
This commit is contained in:
parent
68db4f9b59
commit
846c806034
12 changed files with 277 additions and 126 deletions
|
@ -6,24 +6,39 @@
|
|||
<link rel="stylesheet" href="../css/style.css">
|
||||
<script type="text/javascript" src="../../dist/paper-full.js"></script>
|
||||
<script type="text/paperscript" canvas="canvas1">
|
||||
var ellipse = new Shape.Ellipse({
|
||||
from: [10, 10],
|
||||
to: [200, 100],
|
||||
var circle = new Shape.Circle({
|
||||
center: [100, 100],
|
||||
radius: 50,
|
||||
fillColor: 'red'
|
||||
});
|
||||
|
||||
var circle = new Shape.Circle({
|
||||
center: [50, 150],
|
||||
radius: 25,
|
||||
fillColor: 'blue'
|
||||
var ellipse = new Shape.Ellipse({
|
||||
center: [100, 200],
|
||||
radius: [50, 25],
|
||||
fillColor: 'blue',
|
||||
strokeColor: 'black',
|
||||
strokeWidth: 4,
|
||||
rotation: 20
|
||||
});
|
||||
|
||||
var rectangle = new Shape.Rectangle({
|
||||
from: [25, 200],
|
||||
to: [100, 225],
|
||||
fillColor: 'green'
|
||||
var rect = new Shape.Rectangle({
|
||||
center: [100, 300],
|
||||
size: [100, 50],
|
||||
fillColor: 'green',
|
||||
strokeColor: 'black',
|
||||
strokeWidth: 4,
|
||||
rotation: -20
|
||||
});
|
||||
|
||||
var roundRect = new Shape.Rectangle({
|
||||
center: [100, 400],
|
||||
size: [50, 100],
|
||||
radius: [15, 20],
|
||||
fillColor: 'orange',
|
||||
strokeColor: 'black',
|
||||
strokeWidth: 4,
|
||||
rotation: 20
|
||||
});
|
||||
rectangle.rotate(30);
|
||||
|
||||
window._json = project.exportJSON();
|
||||
console.log(window._json);
|
||||
|
|
|
@ -6,55 +6,46 @@
|
|||
<link rel="stylesheet" href="../css/style.css">
|
||||
<script type="text/javascript" src="../../dist/paper-full.js"></script>
|
||||
<script type="text/paperscript" canvas="canvas">
|
||||
var path = new Path.Circle({
|
||||
var circle = new Shape.Circle({
|
||||
center: [100, 100],
|
||||
radius: 50,
|
||||
fillColor: 'red'
|
||||
});
|
||||
var shape = path.toShape();
|
||||
shape.position += [200, 0];
|
||||
var path = shape.toPath();
|
||||
path.position += [200, 0];
|
||||
|
||||
var path = new Path.Ellipse({
|
||||
var ellipse = new Shape.Ellipse({
|
||||
center: [100, 200],
|
||||
radius: [50, 25],
|
||||
fillColor: 'blue',
|
||||
strokeColor: 'black',
|
||||
strokeWidth: 10
|
||||
strokeWidth: 10,
|
||||
rotation: 20
|
||||
});
|
||||
path.rotate(20);
|
||||
var shape = path.toShape();
|
||||
shape.position += [200, 0];
|
||||
var path = shape.toPath();
|
||||
path.position += [200, 0];
|
||||
|
||||
var path = new Path.Rectangle({
|
||||
var rect = new Shape.Rectangle({
|
||||
center: [100, 300],
|
||||
size: [100, 50],
|
||||
fillColor: 'green',
|
||||
strokeColor: 'black',
|
||||
strokeWidth: 10
|
||||
strokeWidth: 10,
|
||||
rotation: -20
|
||||
});
|
||||
path.rotate(-20);
|
||||
var shape = path.toShape();
|
||||
shape.position += [200, 0];
|
||||
var path = shape.toPath();
|
||||
path.position += [200, 0];
|
||||
|
||||
var path = new Path.Rectangle({
|
||||
var roundRect = new Shape.Rectangle({
|
||||
center: [100, 400],
|
||||
size: [50, 100],
|
||||
radius: [5, 10],
|
||||
radius: [15, 20],
|
||||
fillColor: 'orange',
|
||||
strokeColor: 'black',
|
||||
strokeWidth: 10
|
||||
strokeWidth: 10,
|
||||
rotation: 20
|
||||
});
|
||||
path.rotate(20);
|
||||
var shape = path.toShape();
|
||||
shape.position += [200, 0];
|
||||
var path = shape.toPath();
|
||||
path.position += [200, 0];
|
||||
|
||||
[circle, ellipse, rect, roundRect].forEach(function(shape) {
|
||||
var path = shape.toPath();
|
||||
path.position += [200, 0];
|
||||
var shape2 = path.toShape();
|
||||
shape2.position += [200, 0];
|
||||
})
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
|
66
examples/Scripts/StrokeScaling.html
Normal file
66
examples/Scripts/StrokeScaling.html
Normal file
|
@ -0,0 +1,66 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Shapes</title>
|
||||
<link rel="stylesheet" href="../css/style.css">
|
||||
<script type="text/javascript" src="../../dist/paper-full.js"></script>
|
||||
<script type="text/paperscript" canvas="canvas">
|
||||
// view._context = new ProxyContext(view._context);
|
||||
// view.zoom = 1.25;
|
||||
settings.applyMatrix = false;
|
||||
var path = new Path.Circle({
|
||||
center: view.center - [0, 140],
|
||||
radius: 50,
|
||||
fillColor: 'red',
|
||||
strokeColor: 'black',
|
||||
strokeWidth: 10,
|
||||
strokeScaling: false,
|
||||
opacity: 0.5,
|
||||
selected: true
|
||||
});
|
||||
path.scale(2, 1);
|
||||
|
||||
var shape = new Shape.Circle({
|
||||
center: view.center,
|
||||
radius: 50,
|
||||
fillColor: 'red',
|
||||
strokeColor: 'black',
|
||||
strokeWidth: 10,
|
||||
strokeScaling: false,
|
||||
opacity: 0.5,
|
||||
selected: true
|
||||
});
|
||||
shape.scale(2, 1);
|
||||
|
||||
var hole;
|
||||
var compound = new CompoundPath({
|
||||
children: [
|
||||
new Path.Rectangle({
|
||||
point: [0, 0],
|
||||
size: [100, 100]
|
||||
}),
|
||||
hole = new Path.Circle({
|
||||
center: [50, 50],
|
||||
radius: 25
|
||||
})
|
||||
],
|
||||
fillColor: 'red',
|
||||
strokeColor: 'black',
|
||||
strokeWidth: 10,
|
||||
position: view.center + [0, 140],
|
||||
strokeScaling: false,
|
||||
opacity: 0.5,
|
||||
selected: true
|
||||
});
|
||||
hole.position += 15;
|
||||
compound.scale(2, 1);
|
||||
|
||||
document.getElementById('svg').appendChild(project.exportSVG());
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="canvas" width="300" height="500"></canvas>
|
||||
<svg id="svg" width="300" height="500"></svg>
|
||||
</body>
|
||||
</html>
|
|
@ -437,7 +437,7 @@ var Matrix = Base.extend(/** @lends Matrix# */{
|
|||
* as x, y value pairs
|
||||
* @param {Number[]} dst the array into which to store the transformed
|
||||
* point pairs
|
||||
* @param {Number} count the number of points to tranform
|
||||
* @param {Number} count the number of points to transform
|
||||
* @return {Number[]} the dst array, containing the transformed coordinates.
|
||||
*/
|
||||
transform: function(/* point | */ src, dst, count) {
|
||||
|
|
|
@ -2546,8 +2546,8 @@ var Item = Base.extend(Callback, /** @lends Item# */{
|
|||
*/
|
||||
|
||||
/**
|
||||
* The shape to be used at the end of open {@link Path} items, when they
|
||||
* have a stroke.
|
||||
* The shape to be used at the beginning and end of open {@link Path} items,
|
||||
* when they have a stroke.
|
||||
*
|
||||
* @name Item#strokeCap
|
||||
* @property
|
||||
|
@ -2579,7 +2579,8 @@ var Item = Base.extend(Callback, /** @lends Item# */{
|
|||
*/
|
||||
|
||||
/**
|
||||
* The shape to be used at the corners of paths when they have a stroke.
|
||||
* The shape to be used at the segments and corners of {@link Path} items
|
||||
* when they have a stroke.
|
||||
*
|
||||
* @name Item#strokeJoin
|
||||
* @property
|
||||
|
@ -2615,6 +2616,17 @@ var Item = Base.extend(Callback, /** @lends Item# */{
|
|||
* @type Number
|
||||
*/
|
||||
|
||||
/**
|
||||
* Specifies whether the stroke is to be drawn taking the current affine
|
||||
* transformation into account (the default behavior), or whether it should
|
||||
* appear as a non-scaling stroke.
|
||||
*
|
||||
* @name Style#strokeScaling
|
||||
* @property
|
||||
* @default true
|
||||
* @type Boolean
|
||||
*/
|
||||
|
||||
/**
|
||||
* Specifies an array containing the dash and gap lengths of the stroke.
|
||||
*
|
||||
|
@ -3574,7 +3586,7 @@ var Item = Base.extend(Callback, /** @lends Item# */{
|
|||
}
|
||||
},
|
||||
|
||||
draw: function(ctx, param) {
|
||||
draw: function(ctx, param, parentStrokeMatrix) {
|
||||
// Each time the project gets drawn, it's _updateVersion is increased.
|
||||
// Keep the _updateVersion of drawn items in sync, so we have an easy
|
||||
// way to know for which selected items we need to draw selection info.
|
||||
|
@ -3631,7 +3643,9 @@ var Item = Base.extend(Callback, /** @lends Item# */{
|
|||
// If native blending is possible, see if the item allows it
|
||||
|| (nativeBlend || normalBlend && opacity < 1)
|
||||
&& this._canComposite(),
|
||||
pixelRatio = param.pixelRatio,
|
||||
mainCtx, itemOffset, prevOffset;
|
||||
|
||||
if (!direct) {
|
||||
// Apply the parent's global matrix to the calculation of correct
|
||||
// bounds.
|
||||
|
@ -3648,28 +3662,48 @@ var Item = Base.extend(Callback, /** @lends Item# */{
|
|||
// it, instead of the mainCtx.
|
||||
mainCtx = ctx;
|
||||
ctx = CanvasProvider.getContext(
|
||||
bounds.getSize().ceil().add(new Size(1, 1)),
|
||||
param.pixelRatio);
|
||||
bounds.getSize().ceil().add(new Size(1, 1)), pixelRatio);
|
||||
}
|
||||
ctx.save();
|
||||
// Get the transformation matrix for non-scaling strokes.
|
||||
var strokeMatrix = parentStrokeMatrix
|
||||
? parentStrokeMatrix.clone().concatenate(matrix)
|
||||
: !this.getStrokeScaling() && getViewMatrix(globalMatrix),
|
||||
// If we're drawing into a separate canvas and a clipItem is defined
|
||||
// for the current rendering loop, we need to draw the clip item
|
||||
// again.
|
||||
clip = !direct && param.clipItem,
|
||||
// If we're drawing with a strokeMatrix, the CTM is reset either way
|
||||
// so we don't need to set it, except when we also have to draw a
|
||||
// clipItem.
|
||||
transform = !strokeMatrix || clip;
|
||||
// If drawing directly, handle opacity and native blending now,
|
||||
// otherwise we will do it later when the temporary canvas is composited.
|
||||
if (direct) {
|
||||
ctx.globalAlpha = opacity;
|
||||
if (nativeBlend)
|
||||
ctx.globalCompositeOperation = blendMode;
|
||||
} else {
|
||||
} else if (transform) {
|
||||
// Translate the context so the topLeft of the item is at (0, 0)
|
||||
// on the temporary canvas.
|
||||
ctx.translate(-itemOffset.x, -itemOffset.y);
|
||||
}
|
||||
// Apply globalMatrix when drawing into temporary canvas.
|
||||
(direct ? matrix : getViewMatrix(globalMatrix)).applyToContext(ctx);
|
||||
// If we're drawing into a separate canvas and a clipItem is defined for
|
||||
// the current rendering loop, we need to draw the clip item again.
|
||||
if (!direct && param.clipItem)
|
||||
if (transform)
|
||||
(direct ? matrix : getViewMatrix(globalMatrix)).applyToContext(ctx);
|
||||
if (clip)
|
||||
param.clipItem.draw(ctx, param.extend({ clip: true }));
|
||||
this._draw(ctx, param);
|
||||
if (strokeMatrix) {
|
||||
// Reset the transformation but take HiDPI pixel ratio into account.
|
||||
ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
|
||||
// Also offset again when drawing non-directly.
|
||||
// NOTE: Don't use itemOffset since offset might be from the parent,
|
||||
// e.g. CompoundPath
|
||||
var offset = param.offset;
|
||||
if (offset)
|
||||
ctx.translate(-offset.x, -offset.y);
|
||||
}
|
||||
this._draw(ctx, param, strokeMatrix);
|
||||
ctx.restore();
|
||||
matrices.pop();
|
||||
if (param.clip && !param.dontFinish)
|
||||
|
@ -3681,7 +3715,7 @@ var Item = Base.extend(Callback, /** @lends Item# */{
|
|||
BlendMode.process(blendMode, ctx, mainCtx, opacity,
|
||||
// Calculate the pixel offset of the temporary canvas to the
|
||||
// main canvas. We also need to factor in the pixel-ratio.
|
||||
itemOffset.subtract(prevOffset).multiply(param.pixelRatio));
|
||||
itemOffset.subtract(prevOffset).multiply(pixelRatio));
|
||||
// Return the temporary context, so it can be reused
|
||||
CanvasProvider.release(ctx);
|
||||
// Restore previous offset.
|
||||
|
|
|
@ -173,55 +173,71 @@ var Shape = Item.extend(/** @lends Shape# */{
|
|||
return path;
|
||||
},
|
||||
|
||||
_draw: function(ctx, param) {
|
||||
_draw: function(ctx, param, strokeMatrix) {
|
||||
var style = this._style,
|
||||
hasFill = style.hasFill(),
|
||||
hasStroke = style.hasStroke(),
|
||||
dontPaint = param.dontFinish || param.clip;
|
||||
dontPaint = param.dontFinish || param.clip,
|
||||
untransformed = !strokeMatrix;
|
||||
if (hasFill || hasStroke || dontPaint) {
|
||||
var radius = this._radius,
|
||||
type = this._type;
|
||||
if (!param.dontStart)
|
||||
ctx.beginPath();
|
||||
if (type === 'circle') {
|
||||
var type = this._type,
|
||||
radius = this._radius,
|
||||
isCircle = type === 'circle';
|
||||
if (untransformed && isCircle) {
|
||||
ctx.arc(0, 0, radius, 0, Math.PI * 2, true);
|
||||
} else {
|
||||
var rx = radius.width,
|
||||
ry = radius.height,
|
||||
kappa = /*#=*/ Numerical.KAPPA;
|
||||
if (type === 'ellipse') {
|
||||
// Approximate ellipse with four bezier curves and KAPPA.
|
||||
var cx = rx * kappa,
|
||||
cy = ry * kappa;
|
||||
ctx.moveTo(-rx, 0);
|
||||
ctx.bezierCurveTo(-rx, -cy, -cx, -ry, 0, -ry);
|
||||
ctx.bezierCurveTo(cx, -ry, rx, -cy, rx, 0);
|
||||
ctx.bezierCurveTo(rx, cy, cx, ry, 0, ry);
|
||||
ctx.bezierCurveTo(-cx, ry, -rx, cy, -rx, 0);
|
||||
} else { // rect
|
||||
var size = this._size,
|
||||
width = size.width,
|
||||
height = size.height;
|
||||
if (rx === 0 && ry === 0) {
|
||||
// straight rect
|
||||
ctx.rect(-width / 2, -height / 2, width, height);
|
||||
} else {
|
||||
// rounded rect. Use 1 - KAPPA to calculate position of
|
||||
// control points from the corners inwards.
|
||||
kappa = 1 - kappa;
|
||||
var x = width / 2,
|
||||
y = height / 2,
|
||||
cx = rx * kappa,
|
||||
cy = ry * kappa;
|
||||
ctx.moveTo(-x, -y + ry);
|
||||
ctx.bezierCurveTo(-x, -y + cy, -x + cx, -y, -x + rx, -y);
|
||||
ctx.lineTo(x - rx, -y);
|
||||
ctx.bezierCurveTo(x - cx, -y, x, -y + cy, x, -y + ry);
|
||||
ctx.lineTo(x, y - ry);
|
||||
ctx.bezierCurveTo(x, y - cy, x - cx, y, x - rx, y);
|
||||
ctx.lineTo(-x + rx, y);
|
||||
ctx.bezierCurveTo(-x + cx, y, -x, y - cy, -x, y - ry);
|
||||
}
|
||||
var rx = isCircle ? radius : radius.width,
|
||||
ry = isCircle ? radius : radius.height,
|
||||
size = this._size,
|
||||
width = size.width,
|
||||
height = size.height;
|
||||
if (untransformed && type === 'rect' && rx === 0 && ry === 0) {
|
||||
// Rectangles with no rounding
|
||||
ctx.rect(-width / 2, -height / 2, width, height);
|
||||
} else {
|
||||
// Round rectangles, ellipses, transformed circles
|
||||
var x = width / 2,
|
||||
y = height / 2,
|
||||
// Use 1 - KAPPA to calculate position of control points
|
||||
// from the corners inwards.
|
||||
kappa = 1 - /*#=*/ Numerical.KAPPA,
|
||||
cx = rx * kappa,
|
||||
cy = ry * kappa,
|
||||
// Build the coordinates list, so it can optionally be
|
||||
// transformed by a matrix.
|
||||
c = [
|
||||
-x, -y + ry,
|
||||
-x, -y + cy,
|
||||
-x + cx, -y,
|
||||
-x + rx, -y,
|
||||
x - rx, -y,
|
||||
x - cx, -y,
|
||||
x, -y + cy,
|
||||
x, -y + ry,
|
||||
x, y - ry,
|
||||
x, y - cy,
|
||||
x - cx, y,
|
||||
x - rx, y,
|
||||
-x + rx, y,
|
||||
-x + cx, y,
|
||||
-x, y - cy,
|
||||
-x, y - ry
|
||||
];
|
||||
if (strokeMatrix)
|
||||
strokeMatrix.transform(c, c, 32);
|
||||
if (!param.dontStart)
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(c[0], c[1]);
|
||||
ctx.bezierCurveTo(c[2], c[3], c[4], c[5], c[6], c[7]);
|
||||
if (x !== rx)
|
||||
ctx.lineTo(c[8], c[9]);
|
||||
ctx.bezierCurveTo(c[10], c[11], c[12], c[13], c[14], c[15]);
|
||||
if (y !== ry)
|
||||
ctx.lineTo(c[16], c[17]);
|
||||
ctx.bezierCurveTo(c[18], c[19], c[20], c[21], c[22], c[23]);
|
||||
if (x !== rx)
|
||||
ctx.lineTo(c[24], c[25]);
|
||||
ctx.bezierCurveTo(c[26], c[27], c[28], c[29], c[30], c[31]);
|
||||
}
|
||||
}
|
||||
ctx.closePath();
|
||||
|
|
|
@ -247,7 +247,7 @@ var CompoundPath = PathItem.extend(/** @lends CompoundPath# */{
|
|||
: new Base(options, { fill: false });
|
||||
},
|
||||
|
||||
_draw: function(ctx, param) {
|
||||
_draw: function(ctx, param, strokeMatrix) {
|
||||
var children = this._children;
|
||||
// Return early if the compound path doesn't have any children:
|
||||
if (children.length === 0)
|
||||
|
@ -259,7 +259,7 @@ var CompoundPath = PathItem.extend(/** @lends CompoundPath# */{
|
|||
param = param.extend({ dontStart: true, dontFinish: true });
|
||||
ctx.beginPath();
|
||||
for (var i = 0, l = children.length; i < l; i++)
|
||||
children[i].draw(ctx, param);
|
||||
children[i].draw(ctx, param, strokeMatrix);
|
||||
this._currentPath = ctx.currentPath;
|
||||
}
|
||||
|
||||
|
|
|
@ -1634,7 +1634,7 @@ var Path = PathItem.extend(/** @lends Path# */{
|
|||
|
||||
/**
|
||||
* Returns the curve location of the specified offset on the path.
|
||||
*
|
||||
*
|
||||
* @param {Number} offset the offset on the path, where {@code 0} is at
|
||||
* the beginning of the path and {@link Path#length} at the end.
|
||||
* @param {Boolean} [isParameter=false]
|
||||
|
@ -1997,11 +1997,11 @@ var Path = PathItem.extend(/** @lends Path# */{
|
|||
inX, inY,
|
||||
outX, outY;
|
||||
|
||||
function drawSegment(i) {
|
||||
var segment = segments[i];
|
||||
// Optimise code when no matrix is provided by accessing semgent
|
||||
function drawSegment(segment) {
|
||||
// Optimise code when no matrix is provided by accessing segment
|
||||
// points hand handles directly, since this is the default when
|
||||
// drawing paths. Matrix is only used for drawing selections.
|
||||
// drawing paths. Matrix is only used for drawing selections and
|
||||
// when #strokeScaling is false.
|
||||
if (matrix) {
|
||||
segment._transformCoordinates(matrix, coords, false);
|
||||
curX = coords[0];
|
||||
|
@ -2023,7 +2023,8 @@ var Path = PathItem.extend(/** @lends Path# */{
|
|||
inX = curX + handle._x;
|
||||
inY = curY + handle._y;
|
||||
}
|
||||
if (inX == curX && inY == curY && outX == prevX && outY == prevY) {
|
||||
if (inX === curX && inY === curY
|
||||
&& outX === prevX && outY === prevY) {
|
||||
ctx.lineTo(curX, curY);
|
||||
} else {
|
||||
ctx.bezierCurveTo(outX, outY, inX, inY, curX, curY);
|
||||
|
@ -2042,20 +2043,17 @@ var Path = PathItem.extend(/** @lends Path# */{
|
|||
}
|
||||
|
||||
for (var i = 0; i < length; i++)
|
||||
drawSegment(i);
|
||||
drawSegment(segments[i]);
|
||||
// Close path by drawing first segment again
|
||||
if (path._closed && length > 0)
|
||||
drawSegment(0);
|
||||
drawSegment(segments[0]);
|
||||
}
|
||||
|
||||
return {
|
||||
_draw: function(ctx, param) {
|
||||
_draw: function(ctx, param, strokeMatrix) {
|
||||
var dontStart = param.dontStart,
|
||||
dontPaint = param.dontFinish || param.clip;
|
||||
if (!dontStart)
|
||||
ctx.beginPath();
|
||||
|
||||
var style = this.getStyle(),
|
||||
dontPaint = param.dontFinish || param.clip,
|
||||
style = this.getStyle(),
|
||||
hasFill = style.hasFill(),
|
||||
hasStroke = style.hasStroke(),
|
||||
dashArray = style.getDashArray(),
|
||||
|
@ -2063,18 +2061,15 @@ var Path = PathItem.extend(/** @lends Path# */{
|
|||
dashLength = !paper.support.nativeDash && hasStroke
|
||||
&& dashArray && dashArray.length;
|
||||
|
||||
function getOffset(i) {
|
||||
// Negative modulo is necessary since we're stepping back
|
||||
// in the dash sequence first.
|
||||
return dashArray[((i % dashLength) + dashLength) % dashLength];
|
||||
}
|
||||
if (!dontStart)
|
||||
ctx.beginPath();
|
||||
|
||||
if (!dontStart && this._currentPath) {
|
||||
ctx.currentPath = this._currentPath;
|
||||
} else if (hasFill || hasStroke && !dashLength || dontPaint) {
|
||||
// Prepare the canvas path if we have any situation that
|
||||
// requires it to be defined.
|
||||
drawSegments(ctx, this);
|
||||
drawSegments(ctx, this, strokeMatrix);
|
||||
if (this._closed)
|
||||
ctx.closePath();
|
||||
// CompoundPath collects its own _currentPath
|
||||
|
@ -2082,6 +2077,12 @@ var Path = PathItem.extend(/** @lends Path# */{
|
|||
this._currentPath = ctx.currentPath;
|
||||
}
|
||||
|
||||
function getOffset(i) {
|
||||
// Negative modulo is necessary since we're stepping back
|
||||
// in the dash sequence first.
|
||||
return dashArray[((i % dashLength) + dashLength) % dashLength];
|
||||
}
|
||||
|
||||
if (!dontPaint && (hasFill || hasStroke)) {
|
||||
// If the path is part of a compound path or doesn't have a fill
|
||||
// or stroke, there is no need to continue.
|
||||
|
@ -2102,7 +2103,7 @@ var Path = PathItem.extend(/** @lends Path# */{
|
|||
// native dashes.
|
||||
if (!dontStart)
|
||||
ctx.beginPath();
|
||||
var flattener = new PathFlattener(this),
|
||||
var flattener = new PathFlattener(this, strokeMatrix),
|
||||
length = flattener.length,
|
||||
from = -style.getDashOffset(), to,
|
||||
i = 0;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* @private
|
||||
*/
|
||||
var PathFlattener = Base.extend({
|
||||
initialize: function(path) {
|
||||
initialize: function(path, matrix) {
|
||||
this.curves = []; // The curve values as returned by getValues()
|
||||
this.parts = []; // The calculated, subdivided parts of the path
|
||||
this.length = 0; // The total length of the path
|
||||
|
@ -35,7 +35,7 @@ var PathFlattener = Base.extend({
|
|||
that = this;
|
||||
|
||||
function addCurve(segment1, segment2) {
|
||||
var curve = Curve.getValues(segment1, segment2);
|
||||
var curve = Curve.getValues(segment1, segment2, matrix);
|
||||
that.curves.push(curve);
|
||||
that._computeParts(curve, segment1._index, 0, 1);
|
||||
}
|
||||
|
|
|
@ -77,6 +77,7 @@ var Style = Base.extend(new function() {
|
|||
strokeWidth: 1,
|
||||
strokeCap: 'butt',
|
||||
strokeJoin: 'miter',
|
||||
strokeScaling: true,
|
||||
miterLimit: 10,
|
||||
dashOffset: 0,
|
||||
dashArray: [],
|
||||
|
@ -101,6 +102,8 @@ var Style = Base.extend(new function() {
|
|||
strokeWidth: /*#=*/ Change.STROKE,
|
||||
strokeCap: /*#=*/ Change.STROKE,
|
||||
strokeJoin: /*#=*/ Change.STROKE,
|
||||
// strokeScaling can change the coordinates of cached path items
|
||||
strokeScaling: /*#=*/ Change.STROKE | Change.GEOMETRY,
|
||||
miterLimit: /*#=*/ Change.STROKE,
|
||||
fontFamily: /*#=*/ Change.GEOMETRY,
|
||||
fontWeight: /*#=*/ Change.GEOMETRY,
|
||||
|
@ -369,8 +372,8 @@ var Style = Base.extend(new function() {
|
|||
*/
|
||||
|
||||
/**
|
||||
* The shape to be used at the end of open {@link Path} items, when they
|
||||
* have a stroke.
|
||||
* The shape to be used at the beginning and end of open {@link Path} items,
|
||||
* when they have a stroke.
|
||||
*
|
||||
* @name Style#strokeCap
|
||||
* @property
|
||||
|
@ -402,7 +405,8 @@ var Style = Base.extend(new function() {
|
|||
*/
|
||||
|
||||
/**
|
||||
* The shape to be used at the corners of paths when they have a stroke.
|
||||
* The shape to be used at the segments and corners of {@link Path} items
|
||||
* when they have a stroke.
|
||||
*
|
||||
* @name Style#strokeJoin
|
||||
* @property
|
||||
|
@ -430,6 +434,17 @@ var Style = Base.extend(new function() {
|
|||
* path3.strokeJoin = 'bevel';
|
||||
*/
|
||||
|
||||
/**
|
||||
* Specifies whether the stroke is to be drawn taking the current affine
|
||||
* transformation into account (the default behavior), or whether it should
|
||||
* appear as a non-scaling stroke.
|
||||
*
|
||||
* @name Style#strokeScaling
|
||||
* @property
|
||||
* @default true
|
||||
* @type Boolean
|
||||
*/
|
||||
|
||||
/**
|
||||
* The dash offset of the stroke.
|
||||
*
|
||||
|
|
|
@ -293,8 +293,10 @@ new function() {
|
|||
var get = entry.get,
|
||||
type = entry.type,
|
||||
value = item[get]();
|
||||
if (!parent || !Base.equals(parent[get](), value)) {
|
||||
if (type === 'color' && value != null) {
|
||||
if (entry.exportFilter
|
||||
? entry.exportFilter(item, value)
|
||||
: !parent || !Base.equals(parent[get](), value)) {
|
||||
if (type === 'color' && value !== 'none') {
|
||||
// Support for css-style rgba() values is not in SVG 1.1, so
|
||||
// separate the alpha value of colors with alpha into the
|
||||
// separate fill- / stroke-opacity attribute:
|
||||
|
|
|
@ -18,6 +18,16 @@ var SVGStyles = Base.each({
|
|||
strokeWidth: ['stroke-width', 'number'],
|
||||
strokeCap: ['stroke-linecap', 'string'],
|
||||
strokeJoin: ['stroke-linejoin', 'string'],
|
||||
strokeScaling: ['vector-effect', 'lookup', {
|
||||
true: 'none',
|
||||
false: 'non-scaling-stroke'
|
||||
}, function(item, value) {
|
||||
// no inheritance, only applies to graphical elements
|
||||
return !value // false, meaning non-scaling-stroke
|
||||
&& (item instanceof PathItem
|
||||
|| item instanceof Shape
|
||||
|| item instanceof TextItem);
|
||||
}],
|
||||
miterLimit: ['stroke-miterlimit', 'number'],
|
||||
dashArray: ['stroke-dasharray', 'array'],
|
||||
dashOffset: ['stroke-dashoffset', 'number'],
|
||||
|
@ -44,6 +54,7 @@ var SVGStyles = Base.each({
|
|||
fromSVG: lookup && Base.each(lookup, function(value, name) {
|
||||
this[value] = name;
|
||||
}, {}),
|
||||
exportFilter: entry[3],
|
||||
get: 'get' + part,
|
||||
set: 'set' + part
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue