【问题标题】:HTML5 context.drawImage() crop is not working after calling context.translate()调用 context.translate() 后 HTML5 context.drawImage() 裁剪不起作用
【发布时间】:2015-04-21 08:53:46
【问题描述】:

> 我想要的是 10 幅画布,顶部 5 幅,底部 5 幅,每幅画布的宽度为 17%,高度为 25.5%,之间有适当的间距(见下图)。在画布上绘制的每个图像对应于完整图像的相同区域(见上图)。这有点像目的地输入,但这实际上只是使用 context.translate 和 context.drawImage() 将完整图像裁剪到较小的画布上。请查看最内层 for 循环内的两行与处理 context.translate 和 context.drawImage 的 cmets 以了解可能发生的情况。

请查看我正在尝试使用 html5 context.translate() 和 context.drawImage() 实现的附加图像。

非常感谢任何帮助。谢谢。

    //get parent's width and height	
	var parent = document.getElementById("parent");
	var parentWidth = parent.offsetWidth;
	var parentHeight = parent.offsetHeight;
	
	//get below canvas
	var belowCanvas = document.getElementById('belowCanvas');
	var belowCtx = belowCanvas.getContext('2d');
	
	//create temporary canvas
	var tmpCanvas = document.createElement('canvas');
	var tmpCtx = tmpCanvas.getContext('2d');
	
	//initialize width and height of temporary canvas and below canvas to equal parent
	tmpCanvas.width = belowCanvas.width = parentWidth;
	tmpCanvas.height = belowCanvas.height = parentHeight;
	
	//draw below canvas in black for visual aid of how things are cropped in above canvases
	belowCtx.rect(0,0,parentWidth,parentHeight);
	belowCtx.fillStyle = 'black';
	belowCtx.fill();
	
	//draw temporary canvas
	var centerX = parentWidth/4;
	var centerY = parentHeight/4;
	var radius = parentHeight/4;
	
	tmpCtx.rect(0,0,parentWidth,parentHeight);
	tmpCtx.fillStyle = 'blue';
	tmpCtx.fill();
	
	tmpCtx.beginPath();
	tmpCtx.arc(centerX, centerY, radius*1.5, 0, 2 * Math.PI, false);
	tmpCtx.fillStyle = 'green';
	tmpCtx.fill();
	tmpCtx.lineWidth = 2
	tmpCtx.strokeStyle = '#003300';
	tmpCtx.stroke();
	
	tmpCtx.beginPath();
	tmpCtx.arc(parentWidth - centerX, centerY, radius*1.5, 0, 2 * Math.PI, false);
	tmpCtx.fillStyle = 'purple';
	tmpCtx.fill();
	tmpCtx.lineWidth = 2
	tmpCtx.strokeStyle = '#003300';
	tmpCtx.stroke();
	
	tmpCtx.beginPath();
	tmpCtx.arc(parentWidth/2, parentHeight/2, radius*2, 0, 2 * Math.PI, false);
	tmpCtx.fillStyle = 'white';
	tmpCtx.fill();
	tmpCtx.lineWidth = 2
	tmpCtx.strokeStyle = '#003300';
	tmpCtx.stroke();
	
	tmpCtx.beginPath();
	tmpCtx.arc(centerX, parentHeight - centerY, radius*1.5, 0, 2 * Math.PI, false);
	tmpCtx.fillStyle = 'red';
	tmpCtx.fill();
	tmpCtx.lineWidth = 2
	tmpCtx.strokeStyle = '#003300';
	tmpCtx.stroke();
	
	tmpCtx.beginPath();
	tmpCtx.arc(parentWidth - centerX, parentHeight - centerY, radius*1.5, 0, 2 * Math.PI, false);
	tmpCtx.fillStyle = 'yellow';
	tmpCtx.fill();
	tmpCtx.lineWidth = 2
	tmpCtx.strokeStyle = '#003300';
	tmpCtx.stroke();
	
	//set spacing between canvases
	var horizontalSpacing = parentWidth*0.025
	var verticalSpacing = parentWidth*0.03
	
	//initialize canvases width and height
	var widthCanvas = parentWidth*0.17;
	var heightCanvas = parentWidth*0.255;
	
	var xStart;
	var yStart = verticalSpacing;
	for(var i=0; i< 2; i++)
	{
		xStart = horizontalSpacing;
		for(var j=0; j < 5; j++)
		{
			//get specific destinationInCanvas by id and its respective context
			var canvas = document.getElementById("canvas" + ((i*5)+j));				
			var ctx = canvas.getContext('2d');
			
			canvas.width = widthCanvas;
			canvas.height = heightCanvas;
			
			/***!!!
			Problem next two lines
			if the next line is commented out, each canvas is drawn, but of course not translated; so you only see last canvas, canvas9
			if the next line is NOT commented out, only canvas0 is drawn and translated, the rest of the canvases 1-9 are not drawn*/
			ctx.translate(xStart, yStart);//comment out this line to see effect
			ctx.drawImage(tmpCanvas, xStart, yStart, widthCanvas, heightCanvas, 0, 0, widthCanvas, heightCanvas);
			
			xStart += (horizontalSpacing + widthCanvas);
		}
		yStart += (verticalSpacing + heightCanvas);
	}
	
	#parent {
		width: 1000px;
		height: 600px;
	}

	#belowCanvas{
		position: absolute;
		z-index:-1;
	}

	#canvas0 {
		position: absolute;
		z-index:0;
	}

	#canvas1 {
		position: absolute;
		z-index:1;
	}

	#canvas2 {
		position: absolute;
		z-index:2;
	}

	#canvas3 {
		position: absolute;
		z-index:3;
	}

	#canvas4 {
		position: absolute;
		z-index:4;
	}

	#canvas5 {
		position: absolute;
		z-index:5;
	}

	#canvas6 {
		position: absolute;
		z-index:6;
	}

	#canvas7 {
		position: absolute;
		z-index:7;
	}

	#canvas8 {
		position: absolute;
		z-index:8;
	}

	#canvas9 {
		position: absolute;
		z-index:9;
	}
	<!DOCTYPE HTML>
	<html>
	  <head>
		<link rel="stylesheet" type="text/css" href="style-test.css">
	  </head>
	  <body>
		<div id="parent">
			<canvas id="belowCanvas"></canvas>
			<canvas id="canvas0"></canvas>
			<canvas id="canvas1"></canvas>
			<canvas id="canvas2"></canvas>
			<canvas id="canvas3"></canvas>
			<canvas id="canvas4"></canvas>
			<canvas id="canvas5"></canvas>
			<canvas id="canvas6"></canvas>
			<canvas id="canvas7"></canvas>
			<canvas id="canvas8"></canvas>
			<canvas id="canvas9"></canvas>
		</div>
		<script type="text/javascript" src="script-test.js"></script>
	  </body>
	</html>      

【问题讨论】:

    标签: javascript html


    【解决方案1】:
    • 递增yStartxStart 时遇到问题:
      您应该通过将heightCanvas/widthCanvas 乘以循环的i/j 状态来做到这一点。

    • 另外,在你的 CSS 中,如果你添加 position:absolute 而不设置 marginsleft/top 值,你的画布将进入堆栈,你将只能看到最后一个。仅将position:absolute 添加到您的#belowCanvas。实际上,如果您只需要黑色背景,您可以考虑将所有画布包装成一个divbackground:black;。那么对齐元素会更容易。

    • 最后,您将不需要ctx.translate,因为ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); 已经对源图像进行了翻译(sxsy)。

    编辑在我通过 chat 与 OP 交谈后:
    实际上,他希望画布的孔适合布局,使用 @987654342 创建 @。

    • 我确实删除了 belowCanvas 并将其替换为 div,它的背景通过 CSS 设置为黑色。

    • 为防止您的小画布出现虚边距,您需要设置vertical-align:bottom; float: left,因为浏览器会将画布元素显示为字符。

    • 我重构了代码以编程方式创建小画布,这将允许您更改网格格式。

    例如,我添加了点击监听器来显示/隐藏图层:
    左键单击触发destinationOutLayer右键单击触发小画布层。

    //get parent's width and height 
    var parent = document.getElementById("parent");
    var parentWidth = parent.offsetWidth;
    var parentHeight = parent.offsetHeight;
    
    var destinationOutCanvas = document.getElementById('destinationOutCanvas');
    var destinationOutCtx = destinationOutCanvas.getContext('2d');
    
    //create temporary canvas
    var tmpCanvas = document.createElement('canvas');
    var tmpCtx = tmpCanvas.getContext('2d');
    
    //initialize width and height of temporary canvas and destOut canvas to equal parent
    destinationOutCanvas.width = tmpCanvas.width = parentWidth;
    destinationOutCanvas.height = tmpCanvas.height = parentHeight;
    
    
    //draw temporary canvas
    var centerX = parentWidth / 4;
    var centerY = parentHeight / 4;
    var radius = parentHeight / 4;
    
    tmpCtx.rect(0, 0, parentWidth, parentHeight);
    tmpCtx.fillStyle = 'blue';
    tmpCtx.fill();
    
    tmpCtx.beginPath();
    tmpCtx.arc(centerX, centerY, radius * 1.5, 0, 2 * Math.PI, false);
    tmpCtx.fillStyle = 'green';
    tmpCtx.fill();
    tmpCtx.lineWidth = 2
    tmpCtx.strokeStyle = '#003300';
    tmpCtx.stroke();
    
    tmpCtx.beginPath();
    tmpCtx.arc(parentWidth - centerX, centerY, radius * 1.5, 0, 2 * Math.PI, false);
    tmpCtx.fillStyle = 'purple';
    tmpCtx.fill();
    tmpCtx.lineWidth = 2
    tmpCtx.strokeStyle = '#003300';
    tmpCtx.stroke();
    
    tmpCtx.beginPath();
    tmpCtx.arc(parentWidth / 2, parentHeight / 2, radius * 2, 0, 2 * Math.PI, false);
    tmpCtx.fillStyle = 'white';
    tmpCtx.fill();
    tmpCtx.lineWidth = 2
    tmpCtx.strokeStyle = '#003300';
    tmpCtx.stroke();
    
    tmpCtx.beginPath();
    tmpCtx.arc(centerX, parentHeight - centerY, radius * 1.5, 0, 2 * Math.PI, false);
    tmpCtx.fillStyle = 'red';
    tmpCtx.fill();
    tmpCtx.lineWidth = 2
    tmpCtx.strokeStyle = '#003300';
    tmpCtx.stroke();
    
    tmpCtx.beginPath();
    tmpCtx.arc(parentWidth - centerX, parentHeight - centerY, radius * 1.5, 0, 2 * Math.PI, false);
    tmpCtx.fillStyle = 'yellow';
    tmpCtx.fill();
    tmpCtx.lineWidth = 2
    tmpCtx.strokeStyle = '#003300';
    tmpCtx.stroke();
    
    //set the grid
    //Strange bugs can occur with some fractions (e.g 18 rows);
    var rows = 5;
    var lines = 2;
    
    //set spacing between canvases
    //rounded to the nearest even number, since we divide this by 2 below
    var horizontalSpacing = 2*Math.round((parentWidth*0.025)/2); // or whatever you want
    var verticalSpacing = 2*Math.round((parentHeight*0.03)/2); // or whatever you want
    
    //initialize canvases width and height
    var widthCanvas = 2*(Math.round(parentWidth / rows)/2);
    var heightCanvas = 2*(Math.round(parentHeight / lines)/2);
    
    destinationOutCtx.drawImage(tmpCanvas, 0, 0);
    destinationOutCtx.globalCompositeOperation = 'destination-out';
    destinationOutCtx.fillStyle = 'orange';
    
    for (var i = 0; i < lines; i++) {
    
        for (var j = 0; j < rows; j++) {
    
            //get specific destinationInCanvas by id and its respective context
            //var canvas = document.getElementById("canvas" + ((i * rows) + j));
    
            //create the canvases on the go
            var canvas= document.createElement('canvas');
            canvas.width = widthCanvas;
            canvas.height = heightCanvas;
            var ctx = canvas.getContext('2d');
            
            //only needed for the demonstration toggler
            canvas.className = "small";
            
            //set the transform variables
            var hS = horizontalSpacing/2,
                vS = verticalSpacing/2,
                xStart = (widthCanvas*j)+hS,
                yStart = (heightCanvas*i)+vS,
                cropedWidth = widthCanvas-hS*2,
                cropedHeight = heightCanvas-hS*2;
    
    
            ctx.drawImage(tmpCanvas, xStart, yStart, cropedWidth, cropedHeight, hS, vS, cropedWidth, cropedHeight);
            
            destinationOutCtx.fillRect(xStart, yStart, cropedWidth, cropedHeight);
    
            parent.appendChild(canvas);
        }
    
    }
    
    //Toggle opacity with right and left click
    destinationOutCanvas.addEventListener('click', function () {
        if (this.style.opacity == 0) {
            this.style.opacity = 1
        } else {
            this.style.opacity = 0
        }
    });
    destinationOutCanvas.style.opacity = 1;
    destinationOutCanvas.addEventListener('contextmenu', function (e) {
        console.log('triggered');
        e.preventDefault();
        var smalls = document.querySelectorAll('.small');
        console.log(smalls[0].style.opacity);
        if (smalls[0].style.opacity == 0) {
            for (i = 0; i < smalls.length; i++) {
                smalls[i].style.opacity = 1
            }
        } else {
            for (i = 0; i < smalls.length; i++) {
                smalls[i].style.opacity = 0;
            };
        }
    });
    #parent {
        width: 1000px !important;
        height: 600px;
        background:#000;
    }
    html, body {
        margin:0
    }
    canvas {
        vertical-align:bottom;
        float: left
    }
    .destinationOutLayer, #tmp {
        position: absolute;
        z-index: 2;
        top:0;
    }
    <div id="parent">
    
    </div>
      <canvas id="destinationOutCanvas" class="destinationOutLayer"></canvas>

    【讨论】:

    • 您好 Kaiido,感谢您抽出宝贵时间为我提供此答案。我现在正在处理细节,稍后会发表评论。再次感谢。
    • 我又回到了原来的问题。如果我能想出如何将这个 jsfiddle 使用destination-in (jsfiddle.net/jstafford75/...) 变成这个 jsfiddle 并使用 drawImage 和翻译 (jsfiddle.net/jstafford75/...) 那将是金色的。我会给你积分和疯狂的道具
    • 使用drawImage并像这里jsfiddle.net/jstafford75/f3frwy7f一样翻译,我想用destination-in把它变成这个jsfiddle.net/jstafford75/ts26v6t7
    • Kaiido,感谢您花时间帮助我。当您不确定他们想要完成什么时,很难帮助他们。我很感激你尝试。您关于将我的循环迭代逻辑放在一个地方并且更简洁明了的建议非常好。我在俄克拉荷马城的朋友 Greg Bugaj 帮助我获得了我想要的最终结果。这是一个 CSS “重置”,我不知道该怎么做或如何研究。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-02
    • 1970-01-01
    • 2017-05-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-03
    • 2023-04-07
    相关资源
    最近更新 更多