background clipping support

This commit is contained in:
MoyuScript 2013-01-03 20:34:47 +02:00
parent d29d90b134
commit 1b29b8bc20
4 changed files with 174 additions and 50 deletions

View File

@ -345,10 +345,6 @@ _html2canvas.Parse = function (images, options) {
}); });
} }
function pathArc(x, y, cornerX, cornerY, radius) {
return ["arc", cornerX, cornerY, x, y, radius];
}
var getCurvePoints = (function(kappa) { var getCurvePoints = (function(kappa) {
return function(x, y, r1, r2) { return function(x, y, r1, r2) {
@ -445,6 +441,20 @@ _html2canvas.Parse = function (images, options) {
}; };
} }
function parseCorner(borderArgs, radius1, radius2, corner1, corner2, x, y) {
if (radius1[0] > 0 || radius1[1] > 0) {
borderArgs.push(["line", corner1[0].start.x, corner1[0].start.y]);
corner1[0].curveTo(borderArgs);
corner1[1].curveTo(borderArgs);
} else {
borderArgs.push(["line", x, y]);
}
if (radius2[0] > 0 || radius2[1] > 0) {
borderArgs.push(["line", corner2[0].start.x, corner2[0].start.y]);
}
}
function drawSide(borderData, radius1, radius2, outer1, inner1, outer2, inner2) { function drawSide(borderData, radius1, radius2, outer1, inner1, outer2, inner2) {
var borderArgs = []; var borderArgs = [];
@ -475,10 +485,6 @@ _html2canvas.Parse = function (images, options) {
return borderArgs; return borderArgs;
} }
function getBorderBounds(element) {
var backgroundClip = getCSS(element, 'backgroundClip');
}
function calculateCurvePoints(bounds, borderRadius, borders) { function calculateCurvePoints(bounds, borderRadius, borders) {
var x = bounds.left, var x = bounds.left,
@ -556,30 +562,55 @@ _html2canvas.Parse = function (images, options) {
Math.max(0, blh - borders[3].width), Math.max(0, blh - borders[3].width),
Math.max(0, blv - borders[2].width) Math.max(0, blv - borders[2].width)
).bottomLeft.subdivide(0.5) ).bottomLeft.subdivide(0.5)
} };
} }
function parseBorders(element, ctx, bounds, clip, borders){ function getBorderClip(element, borderPoints, borders, radius, bounds) {
var backgroundClip = getCSS(element, 'backgroundClip'),
borderArgs = [];
switch(backgroundClip) {
case "content-box":
case "padding-box":
parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftInner, borderPoints.topRightInner, bounds.left + borders[3].width, bounds.top + borders[0].width);
parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightInner, borderPoints.bottomRightInner, bounds.left + bounds.width - borders[1].width, bounds.top + borders[0].width);
parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightInner, borderPoints.bottomLeftInner, bounds.left + bounds.width - borders[1].width, bounds.top + bounds.height - borders[2].width);
parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftInner, borderPoints.topLeftInner, bounds.left + borders[3].width, bounds.top + bounds.height - borders[2].width);
break;
default:
parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftOuter, borderPoints.topRightOuter, bounds.left, bounds.top);
parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightOuter, borderPoints.bottomRightOuter, bounds.left + bounds.width, bounds.top);
parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightOuter, borderPoints.bottomLeftOuter, bounds.left + bounds.width, bounds.top + bounds.height);
parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftOuter, borderPoints.topLeftOuter, bounds.left, bounds.top + bounds.height);
break;
}
return borderArgs;
}
function parseBorders(element, bounds, borders){
var x = bounds.left, var x = bounds.left,
y = bounds.top, y = bounds.top,
width = bounds.width, width = bounds.width,
height = bounds.height, height = bounds.height,
borderSide, borderSide,
borderData,
bx, bx,
by, by,
bw, bw,
bh, bh,
borderArgs, borderArgs,
borderBounds,
// http://www.w3.org/TR/css3-background/#the-border-radius // http://www.w3.org/TR/css3-background/#the-border-radius
borderRadius = getBorderRadiusData(element), borderRadius = getBorderRadiusData(element),
borderPoints = calculateCurvePoints(bounds, borderRadius, borders); borderPoints = calculateCurvePoints(bounds, borderRadius, borders),
borderData = {
clip: getBorderClip(element, borderPoints, borders, borderRadius, bounds),
borders: []
};
for (borderSide = 0; borderSide < 4; borderSide++) { for (borderSide = 0; borderSide < 4; borderSide++) {
borderData = borders[borderSide];
borderArgs = []; if (borders[borderSide].width > 0) {
if (borderData.width>0){
bx = x; bx = x;
by = y; by = y;
bw = width; bw = width;
@ -638,33 +669,31 @@ _html2canvas.Parse = function (images, options) {
break; break;
} }
borderBounds = { borderData.borders.push({
left:bx, args: borderArgs,
top:by, color: borders[borderSide].color
width: bw, });
height:bh
};
if (clip){
borderBounds = clipBounds(borderBounds, clip);
}
renderBorders(ctx, borderArgs, borderBounds, borderData.color);
} }
} }
return borders; return borderData;
} }
function renderBorders(ctx, borderArgs, borderBounds, color) { function createShape(ctx, args) {
if (borderBounds.width > 0 && borderBounds.height > 0) {
if (color !== "transparent") {
ctx.setVariable( "fillStyle", color);
var shape = ctx.drawShape(); var shape = ctx.drawShape();
borderArgs.forEach(function(border, index) { args.forEach(function(border, index) {
shape[(index === 0) ? "moveTo" : border[0] + "To" ].apply(null, border.slice(1)); shape[(index === 0) ? "moveTo" : border[0] + "To" ].apply(null, border.slice(1));
}); });
numDraws+=1; return shape;
} }
function renderBorders(ctx, borderArgs, color) {
if (color !== "transparent") {
ctx.setVariable( "fillStyle", color);
createShape(ctx, borderArgs);
ctx.fill();
numDraws+=1;
} }
} }
@ -930,14 +959,23 @@ _html2canvas.Parse = function (images, options) {
stack = createStack(element, parentStack, bounds), stack = createStack(element, parentStack, bounds),
borders = stack.borders, borders = stack.borders,
ctx = stack.ctx, ctx = stack.ctx,
backgroundBounds = getBackgroundBounds(borders, bounds, stack.clip); backgroundBounds = getBackgroundBounds(borders, bounds, stack.clip),
borderData = parseBorders(element, bounds, borders),
clipPath = createShape(ctx, borderData.clip);
ctx.save();
ctx.clip();
if (backgroundBounds.height > 0 && backgroundBounds.width > 0){ if (backgroundBounds.height > 0 && backgroundBounds.width > 0){
renderBackgroundColor(ctx, backgroundBounds, bgcolor); renderBackgroundColor(ctx, bounds, bgcolor);
renderBackgroundImage(element, backgroundBounds, ctx); renderBackgroundImage(element, backgroundBounds, ctx);
} }
parseBorders(element, ctx, bounds, stack.clip, borders); ctx.restore();
borderData.borders.forEach(function(border) {
renderBorders(ctx, border.args, border.color);
});
switch(element.nodeName){ switch(element.nodeName){
case "IMG": case "IMG":

View File

@ -4,6 +4,34 @@ function h2cRenderContext(width, height) {
storage: storage, storage: storage,
width: width, width: width,
height: height, height: height,
clip: function() {
storage.push({
type: "function",
name: "clip",
'arguments': arguments
});
},
fill: function() {
storage.push({
type: "function",
name: "fill",
'arguments': arguments
});
},
save: function() {
storage.push({
type: "function",
name: "save",
'arguments': arguments
});
},
restore: function() {
storage.push({
type: "function",
name: "restore",
'arguments': arguments
});
},
fillRect: function () { fillRect: function () {
storage.push({ storage.push({
type: "function", type: "function",

View File

@ -98,16 +98,12 @@ _html2canvas.Renderer.Canvas = function( options ) {
ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = fstyle; ctx.fillStyle = fstyle;
var drawShape = function(args) { var createShape = function(args) {
var i, len = args.length;
ctx.beginPath(); ctx.beginPath();
for ( i = 0; i < len; i++ ) { args.forEach(function(arg) {
ctx[ args[ i ].name ].apply( ctx, args[ i ]['arguments'] ); ctx[arg.name].apply(ctx, arg['arguments']);
} });
ctx.closePath(); ctx.closePath();
ctx.fill();
}; };
if ( options.svgRendering && zStack.svgRender !== undefined ) { if ( options.svgRendering && zStack.svgRender !== undefined ) {
@ -150,7 +146,7 @@ _html2canvas.Renderer.Canvas = function( options ) {
ctx.fillRect.apply( ctx, renderItem['arguments'] ); ctx.fillRect.apply( ctx, renderItem['arguments'] );
} }
} else if (renderItem.name === "drawShape") { } else if (renderItem.name === "drawShape") {
drawShape(renderItem['arguments']); createShape(renderItem['arguments']);
} else if (renderItem.name === "fillText") { } else if (renderItem.name === "fillText") {
if (!usingFlashcanvas || renderItem['arguments'][1] < flashMaxSize && renderItem['arguments'][2] < flashMaxSize) { if (!usingFlashcanvas || renderItem['arguments'][1] < flashMaxSize && renderItem['arguments'][2] < flashMaxSize) {
ctx.fillText.apply( ctx, renderItem['arguments'] ); ctx.fillText.apply( ctx, renderItem['arguments'] );
@ -175,6 +171,8 @@ _html2canvas.Renderer.Canvas = function( options ) {
} }
ctx.drawImage.apply( ctx, renderItem['arguments'] ); ctx.drawImage.apply( ctx, renderItem['arguments'] );
} }
} else {
ctx[renderItem.name].apply(ctx, renderItem['arguments']);
} }

View File

@ -0,0 +1,60 @@
<!DOCTYPE html>
<html>
<head>
<title>Background attribute tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<style>
html {
background-color: red;
}
body {
background-color: lime;
}
.small div{
width:100px;
height:100px;
float:left;
margin:10px;
border:1px solid #000;
}
.medium div{
width:200px;
height:200px;
float:left;
margin:10px;
border:20px solid transparent;
border-width: 10px 20px 30px 40px;
background: green;
}
.small, .medium{
clear:both;
}
div{
display:block;
}
</style>
</head>
<body>
<div class="medium">
<div style="background:url(../../assets/image.jpg);background-clip: border-box;"></div>
<div style="background:url(../../assets/image.jpg);background-clip: padding-box;"></div>
<div style="background:url(../../assets/image.jpg);background-clip: content-box;"></div>
<div style="background:url(../../assets/image.jpg);"></div>
</div>
<div class="medium">
<div style="background-clip: border-box;"></div>
<div style="background-clip: padding-box;"></div>
<div style="background-clip: content-box;"></div>
<div style=""></div>
</div>
</body>
</html>