initial border-radius rendering

This commit is contained in:
Niklas von Hertzen 2013-01-02 21:26:24 +02:00
parent 42abcfe5fc
commit bb73d3c15e
3 changed files with 273 additions and 82 deletions

View File

@ -330,105 +330,293 @@ _html2canvas.Parse = function (images, options) {
);
}
function renderBorders(el, ctx, bounds, clip){
function getBorderData(element) {
return ["Top", "Right", "Bottom", "Left"].map(function(side) {
return {
width: getCSSInt(element, 'border' + side + 'Width'),
color: getCSS(element, 'border' + side + 'Color')
};
});
}
function getBorderRadiusData(element) {
return ["TopLeft", "TopRight", "BottomRight", "BottomLeft"].map(function(side) {
return getCSS(element, 'border' + side + 'Radius');
});
}
function pathArc(x, y, cornerX, cornerY, radius) {
return ["arc", cornerX, cornerY, x, y, radius];
}
var getCurvePoints = (function(kappa) {
return function(x, y, r1, r2) {
var ox = (r1) * kappa, // control point offset horizontal
oy = (r2) * kappa, // control point offset vertical
xm = x + r1, // x-middle
ym = y + r2; // y-middle
return {
topLeft: bezierCurve({
x:x,
y:ym
}, {
x:x,
y:ym - oy
}, {
x:xm - ox,
y:y
}, {
x:xm,
y:y
}),
topRight: bezierCurve({
x:x,
y:y
}, {
x:x + ox,
y:y
}, {
x:xm,
y:ym - oy
}, {
x:xm,
y:ym
}),
bottomRight: bezierCurve({
x:xm,
y:y
}, {
x:xm,
y:y + oy
}, {
x:x + ox,
y:ym
}, {
x:x,
y:ym
}),
bottomLeft: bezierCurve({
x:xm,
y:ym
}, {
x:xm - ox,
y:ym
}, {
x:x,
y:y + oy
}, {
x:x,
y:y
})
};
};
})(4 * ((Math.sqrt(2) - 1) / 3));
function bezierCurve(start, startControl, endControl, end) {
var lerp = function (a, b, t) {
return {
x:a.x + (b.x - a.x) * t,
y:a.y + (b.y - a.y) * t
};
};
return {
start: start,
startControl: startControl,
endControl: endControl,
end: end,
subdivide: function(t) {
var ab = lerp(start, startControl, t),
bc = lerp(startControl, endControl, t),
cd = lerp(endControl, end, t),
abbc = lerp(ab, bc, t),
bccd = lerp(bc, cd, t),
dest = lerp(abbc, bccd, t);
return [bezierCurve(start, ab, abbc, dest), bezierCurve(dest, bccd, cd, end)];
},
curveTo: function(borderArgs) {
borderArgs.push(["bezierCurve", startControl.x, startControl.y, endControl.x, endControl.y, end.x, end.y]);
},
curveToReversed: function(borderArgs) {
borderArgs.push(["bezierCurve", endControl.x, endControl.y, startControl.x, startControl.y, start.x, start.y]);
}
};
}
function drawSide(borderData, radius1, radius2, outer1, inner1, outer2, inner2) {
var borderArgs = [];
if (radius1[0] > 0 || radius1[1] > 0) {
borderArgs.push(["line", outer1[1].start.x, outer1[1].start.y]);
outer1[1].curveTo(borderArgs);
} else {
borderArgs.push([ "line", borderData.c1[0], borderData.c1[1]]);
}
if (radius2[0] > 0 || radius2[1] > 0) {
borderArgs.push(["line", outer2[0].start.x, outer2[0].start.y]);
outer2[0].curveTo(borderArgs);
borderArgs.push(["line", inner2[0].end.x, inner2[0].end.y]);
inner2[0].curveToReversed(borderArgs);
} else {
borderArgs.push([ "line", borderData.c2[0], borderData.c2[1]]);
borderArgs.push([ "line", borderData.c3[0], borderData.c3[1]]);
}
if (radius1[0] > 0 || radius1[1] > 0) {
borderArgs.push(["line", inner1[1].end.x, inner1[1].end.y]);
inner1[1].curveToReversed(borderArgs);
} else {
borderArgs.push([ "line", borderData.c4[0], borderData.c4[1]]);
}
return borderArgs;
}
function parseBorders(element, ctx, bounds, clip, borders){
var x = bounds.left,
y = bounds.top,
w = bounds.width,
h = bounds.height,
width = bounds.width,
height = bounds.height,
borderSide,
borderData,
bx,
by,
bw,
bh,
i,
borderArgs,
borderBounds,
borders = (function(el){
var borders = [],
sides = ["Top","Right","Bottom","Left"],
s;
for (s = 0; s < 4; s+=1){
borders.push({
width: getCSSInt(el, 'border' + sides[s] + 'Width'),
color: getCSS(el, 'border' + sides[s] + 'Color')
});
}
return borders;
}(el)),
// http://www.w3.org/TR/css3-background/#the-border-radius
borderRadius = (function( el ) {
var borders = [],
sides = ["TopLeft","TopRight","BottomRight","BottomLeft"],
s;
for (s = 0; s < 4; s+=1){
borders.push( getCSS(el, 'border' + sides[s] + 'Radius') );
}
return borders;
})( el );
borderRadius = getBorderRadiusData(element);
var tlh = borderRadius[0][0];
var tlv = borderRadius[0][1];
var trh = borderRadius[1][0];
var trv = borderRadius[1][1];
var brv = borderRadius[2][0];
var brh = borderRadius[2][1];
var blh = borderRadius[3][0];
var blv = borderRadius[3][1];
for ( borderSide = 0; borderSide < 4; borderSide+=1 ) {
borderData = borders[ borderSide ];
var topWidth = width - trh;
var rightHeight = height - brv;
var bottomWidth = width - brh;
var leftHeight = height - blv;
var topLeftOuter = getCurvePoints(
x,
y,
tlh,
tlv
).topLeft.subdivide(0.5);
var topLeftInner = getCurvePoints(
x + borders[3].width,
y + borders[0].width,
Math.max(0, tlh - borders[3].width),
Math.max(0, tlv - borders[0].width)
).topLeft.subdivide(0.5);
var topRightOuter = getCurvePoints(
x + topWidth,
y,
trh,
trv
).topRight.subdivide(0.5);
var topRightInner = getCurvePoints(
x + Math.min(topWidth, width + borders[3].width),
y + borders[0].width,
(topWidth > width + borders[3].width) ? 0 :trh - borders[3].width,
trv - borders[0].width
).topRight.subdivide(0.5);
var bottomRightOuter = getCurvePoints(
x + bottomWidth,
y + rightHeight,
brh,
brv
).bottomRight.subdivide(0.5);
var bottomRightInner = getCurvePoints(
x + Math.min(bottomWidth, width + borders[3].width),
y + Math.min(rightHeight, height + borders[0].width),
Math.max(0, brh - borders[1].width),
Math.max(0, brv - borders[2].width)
).bottomRight.subdivide(0.5);
var bottomLeftOuter = getCurvePoints(
x,
y + leftHeight,
blh,
blv
).bottomLeft.subdivide(0.5);
var bottomLeftInner = getCurvePoints(
x + borders[3].width,
y + leftHeight,
Math.max(0, blh - borders[3].width),
Math.max(0, blv - borders[2].width)
).bottomLeft.subdivide(0.5);
for (borderSide = 0; borderSide < 4; borderSide++) {
borderData = borders[borderSide];
borderArgs = [];
if (borderData.width>0){
bx = x;
by = y;
bw = w;
bh = h - (borders[2].width);
bw = width;
bh = height - (borders[2].width);
switch(borderSide){
case 0:
// top border
bh = borders[0].width;
i = 0;
borderArgs[ i++ ] = [ "line", bx, by ]; // top left
borderArgs[ i++ ] = [ "line", bx + bw, by ]; // top right
borderArgs[ i++ ] = [ "line", bx + bw - borders[ 1 ].width, by + bh ]; // bottom right
borderArgs[ i++ ] = [ "line", bx + borders[ 3 ].width, by + bh ]; // bottom left
borderArgs = drawSide({
c1: [bx, by],
c2: [bx + bw, by],
c3: [bx + bw - borders[1].width, by + bh],
c4: [bx + borders[3].width, by + bh]
}, borderRadius[0], borderRadius[1], topLeftOuter, topLeftInner, topRightOuter, topRightInner);
break;
case 1:
// right border
bx = x + w - (borders[1].width);
bx = x + width - (borders[1].width);
bw = borders[1].width;
i = 0;
borderArgs[ i++ ] = [ "line", bx, by + borders[ 0 ].width]; // top left
borderArgs[ i++ ] = [ "line", bx + bw, by ]; // top right
borderArgs[ i++ ] = [ "line", bx + bw, by + bh + borders[ 2 ].width ]; // bottom right
borderArgs[ i++ ] = [ "line", bx, by + bh ]; // bottom left
borderArgs = drawSide({
c1: [bx + bw, by],
c2: [bx + bw, by + bh + borders[2].width],
c3: [bx, by + bh],
c4: [bx, by + borders[0].width]
}, borderRadius[1], borderRadius[2], topRightOuter, topRightInner, bottomRightOuter, bottomRightInner);
break;
case 2:
// bottom border
by = (by + h) - (borders[2].width);
by = (by + height) - (borders[2].width);
bh = borders[2].width;
i = 0;
borderArgs[ i++ ] = [ "line", bx + borders[ 3 ].width, by ]; // top left
borderArgs[ i++ ] = [ "line", bx + bw - borders[ 2 ].width, by ]; // top right
borderArgs[ i++ ] = [ "line", bx + bw, by + bh ]; // bottom right
borderArgs[ i++ ] = [ "line", bx, by + bh ]; // bottom left
borderArgs = drawSide({
c1: [bx + bw, by + bh],
c2: [bx, by + bh],
c3: [bx + borders[3].width, by],
c4: [bx + bw - borders[2].width, by]
}, borderRadius[2], borderRadius[3], bottomRightOuter, bottomRightInner, bottomLeftOuter, bottomLeftInner);
break;
case 3:
// left border
bw = borders[3].width;
i = 0;
borderArgs[ i++ ] = [ "line", bx, by ]; // top left
borderArgs[ i++ ] = [ "line", bx + bw, by + borders[ 0 ].width ]; // top right
borderArgs[ i++ ] = [ "line", bx + bw, by + bh ]; // bottom right
borderArgs[ i++ ] = [ "line", bx, by + bh + borders[ 2 ].width ]; // bottom left
borderArgs = drawSide({
c1: [bx, by + bh + borders[2].width],
c2: [bx, by],
c3: [bx + bw, by + borders[0].width],
c4: [bx + bw, by + bh]
}, borderRadius[3], borderRadius[0], bottomLeftOuter, bottomLeftInner, topLeftOuter, topLeftInner);
break;
}
@ -442,32 +630,25 @@ _html2canvas.Parse = function (images, options) {
if (clip){
borderBounds = clipBounds(borderBounds, clip);
}
if ( borderBounds.width > 0 && borderBounds.height > 0 ) {
if ( borderData.color !== "transparent" ){
ctx.setVariable( "fillStyle", borderData.color );
var shape = ctx.drawShape(),
numBorderArgs = borderArgs.length;
for ( i = 0; i < numBorderArgs; i++ ) {
shape[( i === 0) ? "moveTo" : borderArgs[ i ][ 0 ] + "To" ].apply( null, borderArgs[ i ].slice(1) );
}
numDraws+=1;
}
}
renderBorders(ctx, borderArgs, borderBounds, borderData.color);
}
}
return borders;
}
function renderBorders(ctx, borderArgs, borderBounds, color) {
if (borderBounds.width > 0 && borderBounds.height > 0) {
if (color !== "transparent") {
ctx.setVariable( "fillStyle", color);
var shape = ctx.drawShape();
borderArgs.forEach(function(border, index) {
shape[(index === 0) ? "moveTo" : border[0] + "To" ].apply(null, border.slice(1));
});
numDraws+=1;
}
}
}
function renderFormValue (el, bounds, stack){
@ -695,7 +876,7 @@ _html2canvas.Parse = function (images, options) {
zIndex: setZ(getCSS(element, "zIndex"), (parentStack) ? parentStack.zIndex : null),
opacity: setOpacity(ctx, element, parentStack),
cssPosition: getCSS(element, "position"),
borders: renderBorders(element, ctx, bounds, false),
borders: getBorderData(element),
clip: (parentStack && parentStack.clip) ? _html2canvas.Util.Extend( {}, parentStack.clip ) : null
};
@ -738,6 +919,8 @@ _html2canvas.Parse = function (images, options) {
renderBackgroundImage(element, backgroundBounds, ctx);
}
parseBorders(element, ctx, bounds, stack.clip, borders);
switch(element.nodeName){
case "IMG":
if ((image = loadImage(element.getAttribute('src')))) {

View File

@ -34,6 +34,12 @@ function h2cRenderContext(width, height) {
'arguments': arguments
});
},
arcTo: function() {
shape.push({
name: "arcTo",
'arguments': arguments
});
},
bezierCurveTo: function() {
shape.push({
name: "bezierCurveTo",

View File

@ -31,7 +31,7 @@
.box3 {
border-width: 10px;
border-left-color: #00b5e2;
border-left-color: transparent;
border-top-color: red;
border-right-color: green;
}
@ -40,7 +40,9 @@
border-width: 50px;
border-left-color: #00b5e2;
border-top-color: red;
border-top-width: 10px;
border-right-color: green;
border-bottom-right-radius: 190px;
}
html {