我正在寻找一种方法,只允许拖放图像区域。
因此,如果图像如下:
我可以选择在顶部添加另一个图层,但是有一些文字,但是新图层不能去任何地方,但是在图像的形状内。
因此,经过一些研究,我仍然想知道如何才能实现这样的目标?
我知道我可以使用map
和area
来映射图像上的现有元素,但是如何添加仅适合该地图的新元素?有什么想法吗?
答案 0 :(得分:2)
为了说明您的解决方案,让我们假设您想要在云中放下雨滴,并确保所有雨滴像素都完全在云中......
然后你的问题的答案需要提出这个问题:
所有不透明的雨滴像素是否完全在云中?
要回答这个问题,您必须将雨滴上的每个像素与下面的每个像素进行比较。
您可以通过在画布上绘制图像然后请求getImageData
来获取有关雨滴和云的所需透明度信息。 ' getImageData'返回红色,绿色,蓝色和&有关画布上每个像素的alpha信息。要回答这个问题,我们只需要alpha信息。
这里带注释的代码和演示:
// canvas related variables
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var offsetX,offsetY;
// load the cloud and raindrop images
var cloudmap,rainmap;
var rain=new Image();
rain.crossOrigin='anonymous';
rain.onload=start;
rain.src='https://dl.dropboxusercontent.com/u/139992952/raindrop1.png';
var cloud=new Image();
cloud.crossOrigin='anonymous';
cloud.onload=start;
cloud.src="https://dl.dropboxusercontent.com/u/139992952/cloud.png";
var cloud1=new Image();
cloud1.crossOrigin='anonymous';
cloud1.onload=start;
cloud1.src="https://dl.dropboxusercontent.com/u/139992952/multple/cloud1.png";
var imageCount=3;
function start(){
if(--imageCount>0){return;}
// resize the canvas to the size of the cloud
// and draw the cloud on the canvas
cw=canvas.width=cloud.width;
ch=canvas.height=cloud.height;
draw();
// create a transparency map of the cloud
cloudmap={
width:cloud.width,
height:cloud.height,
map:transparencyMap(cloud),
};
// create a transparency map of the raindrop
rainmap={
width:rain.width,
height:rain.height,
map:transparencyMap(rain),
}
// listen for mousemove events
$("#canvas").mousemove(function(e){handleMouseMove(e);});
// listen for window scroll events
calcCanvasOffset();
$(window).scroll(function(){ calcCanvasOffset(); });
}
function transparencyMap(img){
// create a temp canvas sized to the img size
var c=document.createElement('canvas');
var cctx=c.getContext('2d');
c.width=img.width;
c.height=img.height;
// draw the img on the canvas
cctx.drawImage(img,0,0);
// get the pixel data for every pixel on the canvas
var data=cctx.getImageData(0,0,c.width,c.height).data;
// create an array that reports the status
// of every pixel on the canvas
// (status: true if opaque, false if transparent)
var map=[];
for(var i=0;i<data.length;i+=4){
map.push(data[i+3]>250);
}
return(map);
}
function draw(mouseX,mouseY,isContained){
// draw the cloud
ctx.clearRect(0,0,cw,ch);
if(isContained){
// draw the blue cloud indicating the raindrop is not fully contained
ctx.drawImage(cloud,0,0);
}else{
// draw the yellow cloud indicating the raindrop is fully contained
ctx.drawImage(cloud1,0,0);
}
// if the mouse position was supplied
if(mouseX){
ctx.drawImage(rain,mouseX-rain.width/2,mouseY-rain.height/2);
}
}
function AcontainsB(ax,ay,amap,bx,by,bmap){
// set a flag indicating of the raindrop is fully contained in the cloud
var isContained=true;
// calc the relative position of the raindrop vs cloud in the canvas
var deltaX=bx-ax;
var deltaY=by-ay;
// test every pixel of B against A
// if B is opaque and a is not opaque then B is not contained by A
var y=0;
while(isContained && y<bmap.height){
var x=0;
while(isContained && x<bmap.width){
// calc the map array indexes for the cloud(A) & raindrop(B)
var mapIndexA=(y+deltaY)*amap.width+(x+deltaX);
var mapIndexB=y*bmap.width+x;
// if the raindrop is opaque at this pixel
if(bmap.map[mapIndexB]){
// ...and if this pixel is off canvas
if(mapIndexA<0 || mapIndexA>=amap.map.length){
// ...then the raindrop is not in the cloud at this pixel
isContained=false;
// ...or if the pixel under the raindrop is transparent
}else if(!amap.map[mapIndexA]){
// ...then the raindrop is not in the cloud at this pixel
isContained=false;
}
}
x++;
}
y++;
}
return(isContained);
}
function handleMouseMove(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// get the current mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// calc the top-left corner of the raindrop image
var rainX=parseInt(mouseX-rain.width/2);
var rainY=parseInt(mouseY-rain.height/2);
// ask if the raindrop is fully contained in the cloud
var isContained=AcontainsB(0,0,cloudmap,rainX,rainY,rainmap);
// redraw the cloud & raindrop
draw(mouseX,mouseY,isContained);
}
// recalc the canvas offsetX & offsetY
function calcCanvasOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
&#13;
body{ background-color: ivory; }
canvas{border:1px solid red;}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Use the mouse to drag the raindrop over the canvas<br>The cloud turns blue if the rain is fully inside the cloud</h4>
<canvas id="canvas" width=600 height=500></canvas>
&#13;
您可以使用此&#34;透明度映射&#34;测试任何可以绘制到画布中的内容,包括图像和文本。请注意,文本是使用context.fillText
绘制的。
如果您要删除画布外部的文本元素(可能使用jqueryUI或native&#34; draggable&#34;),则必须:
获取掉落的x,y位置。
获取已删除元素的文本内容。
创建包含文本的临时画布。这样做......
function textToCanvas(text,fontsize,fontface){
var c=document.createElement('canvas');
var cctx=c.getContext('2d');
cctx.font=fontsize+'px '+fontface;
var textWidth=cctx.measureText(text).width;
c.width=textWidth;
c.height=fontsize+4;
cctx.font=fontsize+'px '+fontface;
cctx.textBaseline='top';
cctx.fillText(text,0,0);
return(c);
}
像图像一样使用临时画布来创建透明度贴图。这是可能的,因为画布将接受另一个画布作为其图像源。
祝你的项目好运。
[评论中的其他问题]
其他问题:
&#34;当你将雨滴放在某处时,这将如何反应 云(保存它的位置),然后你尝试添加一些东西 在它之上?&#34;换句话说:&#34;如何测试2个对象是什么? ?重叠&#34;
答案:您可以再次使用透明贴图测试2个对象是否重叠。创建另一个测试(AintersectsB),测试A中的任何像素是否不透明,而B中的关联像素也是不透明的。您可以从AcontainsB
开始修改它以创建AintersectsB
测试。
其他问题:
&#34;如何保存并稍后恢复掉落的物体位置?&#34;
答案:由于画布不记得它画的是什么,你必须记住它。这通常通过为每个已删除的项创建javascript对象并将所有这些对象保存在数组中来完成。这样,如果您需要保存服务器上的位置,可以使用JSON.stringify
将对象数组转换为字符串,并将该字符串发送到服务器以保存在数据库(或文件)中。为了重新创建您的工作,服务器从数据库中提取字符串并将其发送到浏览器。浏览器使用JSON.parse
将字符串转换回javascript对象数组。然后,您可以使用对象中的信息完全重绘场景。