mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
background clipping support
This commit is contained in:
parent
d29d90b134
commit
1b29b8bc20
110
src/Parse.js
110
src/Parse.js
@ -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":
|
||||||
|
28
src/Queue.js
28
src/Queue.js
@ -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",
|
||||||
|
@ -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']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
60
tests/cases/background/clip.html
Normal file
60
tests/cases/background/clip.html
Normal 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>
|
Loading…
x
Reference in New Issue
Block a user