KonvaJS 裁剪和调整大小
KonvaJS crop and resize
我正在使用 KonvaJS 创建一个简单的图像编辑器。作为图像调整大小功能的基础,我使用了 Konva 示例 (Knonva JS Image Resize) 中的代码。但是现在我正在努力实现裁剪功能。单击按钮时,我允许用户在舞台上绘制一个矩形。然后,我使用图像上的内置裁剪功能以及所绘制矩形的坐标、宽度和高度来裁剪图像。但是当我在裁剪图像之前调整图像大小时,裁剪区域显示从原始尺寸图像裁剪的部分。
是否有一种简单的构建方式让我能够从调整大小的图像中裁剪?或者我是否必须根据图像调整后的值计算绘制矩形的位置和大小,然后裁剪该部分并调整大小并重新定位结果?
剪裁图片:
function cropImage(x, y, width, height, activeLayer) {
var image = activeLayer.get('Image')[0], xDiff = 0, yDiff = 0, newWidth, newHeight, newX, newY;
// only Crop visible Parts of the Image
if(x < activeLayer.getX()) {
xDiff = activeLayer.getX() - x;
}
if(y < activeLayer.getY()) {
yDiff = activeLayer.getY() - y;
}
if (x + width > activeLayer.getX() + activeLayer.width()) {
width = width - ((x + width) - (activeLayer.getX() + activeLayer.width()));
}
if (y + height > activeLayer.getY() + activeLayer.height()) {
height = height - ((y + height) - (activeLayer.getY() + activeLayer.height()));
}
newHeight = height - yDiff;
newWidth = width - xDiff;
newX = (x - activeLayer.getX()) + image.cropX() + xDiff;
newY = (y - activeLayer.getY()) + image.cropY() + yDiff;
image.width(newWidth);
image.height(newHeight);
activeLayer.width(newWidth);
activeLayer.height(newHeight);
activeLayer.setX(newX + activeLayer.getX() - image.cropX());
activeLayer.setY(newY + activeLayer.getY() - image.cropY());
image.crop({
x : newX ,
y : newY ,
width : newWidth,
height : newHeight
});
//Reposition anchors so topLeft Anchor is always in 0/0 of the grouplayer
repositionAnchors(activeLayer);
activeLayer.draw();
}
调整大小时,我在数组中保留宽度和高度的比率,并计算为裁剪绘制的矩形的位置和尺寸:
cropX = ((x / ratios[id].width) - (activeLayer.getX() / ratios[id].width)) + (image.cropX()) + (xDiff / ratios[id].width);
cropY = ((y / ratios[id].height)- (activeLayer.getY() / ratios[id].height)) + (image.cropY()) + (yDiff / ratios[id].height);
cropWidth = (width / ratios[id].width) - (xDiff / ratios[id].width),
cropHeight = (height / ratios[id].height) - (yDiff / ratios[id].height)
这还不是您问题的准确答案,但我将其包含在这里是因为它可能会为您或处于同一种植区的其他人指明方向。
运行 片段全屏。
左图为Konva,右图为原图。
在左侧图像上单击并拖动以进行选择。左侧图像仅更改为选择,而右侧显示裁剪位置。重复这个过程,看看庄稼是如何生长的。
在此示例中,我没有缩放 canvas 上的图像,因此它不能准确回答您目前的需求,但有助于可视化正在发生的事情。如果人们认为它有用,我可以添加缩放。
// Useful frequently used variables.
var sX = 0, sY = 0, sW = 400, sH = 200; // drawing dimensions
var iW = 0, iH = 0; // image dimensions
var cropRect = {x: sX, y: sY, width: iW, height: iH}; // scaled rect
var imgRect = $('.imgRect');
var imgPtr = $('#imgPtr');
var scale = 1;
var img = $('#daImg');
var src = "https://dummyimage.com/400x200/e85de8/fff&text=SO Rocks!"
$('.container').css({width: sW, height: sH});
// Vars for mouse rect work.
var posStart, posNow, mode = '';
// Set up add a stage & layer
var s1 = new Konva.Stage({container: 'container', width: sW, height: sH});
var l1 = new Konva.Layer({});
s1.add(l1);
var image = new Konva.Image({}) // prepare an image to display the picture.
l1.add(image);
// I use a foreground rect to catch events - this covers the konva image completely - you can wire your events in your own way
var r1 = new Konva.Rect({x: 0, y: 0, width: sW, height: sH, fill: 'gold', opacity: 0 })
l1.add(r1)
// draw a rectangle to be used as the rubber-band area
var r2 = new Konva.Rect({x: 0, y: 0, width: 0, height: 0, stroke: 'red', dash: [2,2]})
r2.listening(false); // stop r2 catching our mouse events otherwise if we reverse mouse direction events may not fire
l1.add(r2)
// Mouse movement funcs
function startDrag(posIn){
posStart = {x: posIn.x, y: posIn.y};
posNow = {x: posIn.x, y: posIn.y};
}
// update rubber rect position
function updateDrag(posIn){
posNow = {x: posIn.x, y: posIn.y};
var posRect = reverse(posStart,posNow);
r2.x(posRect.x1);
r2.y(posRect.y1);
r2.width(posRect.x2 - posRect.x1);
r2.height(posRect.y2 - posRect.y1);
r2.visible(true);
s1.draw(); // redraw any changes.
sayRect(r2);
showImgRect(r2);
}
// start the rubber rect drawing on mouse down.
r1.on('mousedown', function(e){
mode = 'drawing';
startDrag({x: e.evt.layerX, y: e.evt.layerY})
})
// update the rubber rect on mouse move - note use of 'mode' var to avoid drawing after mouse released.
r1.on('mousemove', function(e){
if (mode === 'drawing'){
updateDrag({x: e.evt.layerX, y: e.evt.layerY})
}
showImgPtr(e.evt.layerX, e.evt.layerY);
sayPos(e.evt.layerX, e.evt.layerY);
})
// When user releases the mouse we note the size and modify the clip rect.
r1.on('mouseup', function(e){
mode = '';
r2.visible(false);
// leave a rect to show the target
imgRect.hide();
var imgRect2 = imgRect.clone();
imgRect2
.appendTo('#container2')
.addClass('deleteMe')
.show();
setCrop(r2);
sayInfo(img, image);
})
// Draw a rect on the original image to show location and size. Just using some simple jquery to manipulate a div.
function showImgRect(r){
imgRect.css({
left: r.x() + cropRect.x,
top: r.y() + cropRect.y,
width: r.width() * 1,
height: r.height() * 1
})
imgRect.show();
}
// show a mouse pointer on the original image so we get a sense of what is going on
function showImgPtr(x, y){
imgPtr.css({ left: cropRect.x + x, top: cropRect.y + y})
}
// Set the new crop rect, taking account of previous crops
function setCrop(r){
image.cropX(r.x() + cropRect.x);
image.cropY(r.y() + cropRect.y);
image.cropWidth(r.width() * scale);
image.cropHeight(r.height() * scale);
image.width(r.width());
image.height(r.height());
l1.draw();
cropRect = {x: cropRect.x + r.x(), y: cropRect.y + r.y(), width: r.width(), height: r.height()};
}
// This event listener is fired when the image is loaded - could be a few secs delay for a big image
// so this is effectively an async technique.
img.on('load', function() {
// note the dimensions
iW = img.width();
iH = img.height();
// set the konva image details
image.x(sX);
image.y(sY);
image.width(iW);
image.height(iH);
image.image(img[0]);
sayInfo(img, image);
l1.draw(); // redraw the layer to see what happened
});
// This innocent looking line intiates the image load and ultimately fires the event above.
img.prop('src', src);
/*
From here down is utility stuff
*/
// Say something useful
function sayInfo(img, image){
$('#imgInfo').html("HTML Image size " + img.width() + " x " + img.height());
$('#imageInfo').html("Konva.image " + image.x() + ", " + image.y() + " - " + image.width() + " x " + image.height());
var info = $('#info');
}
function sayRect(r){
var rectInfo = $('#rectInfo');
rectInfo.html("Clip rect on canvas " + r.x() + ", " + r.y() + " - " + r.width() + " x " + r.height());
}
function sayPos(x, y){
var posInfo = $('#posInfo');
posInfo.html("Pos on stage " + x + ", " + y);
}
// This is just to reverse co-ords if user drags left / up
function reverse(r1, r2){
var r1x = r1.x, r1y = r1.y, r2x = r2.x, r2y = r2.y, d;
if (r1x > r2x ){
d = Math.abs(r1x - r2x);
r1x = r2x; r2x = r1x + d;
}
if (r1y > r2y ){
d = Math.abs(r1y - r2y);
r1y = r2y; r2y = r1y + d;
}
return ({x1: r1x, y1: r1y, x2: r2x, y2: r2y}); // return the corrected rect.
}
// reset function
function reset(){
sX = 0; sY = 0; sW = 400; sH = 300; // drawing dimensions
iW = 0; iH = 0; // image dimensions
iW = img.width();
iH = img.height();
cropRect = {x: sX, y: sY, width: iW, height: iH}; // scaled rect
scale = 1;
if (image){
console.log('iH=' +iH);
image.x(sX);
image.y(sY);
image.width(iW);
image.height(iH);
image.cropX(sX);
image.cropY(sY);
image.cropWidth(iW);
image.cropHeight(iH);
}
$('.deleteMe').remove();
$('.imgRect').hide();
l1.draw();
}
$('#reset').on('click', function(){reset()});
p
{
padding: 5px;
}
.container {
position: relative;
display: inline-block;
width: 500px;
height: 400px;
background-color: transparent;
overflow: hidden;
border: 1px solid silver;
}
.imgRect {
position: absolute;
border: 1px dotted red;
background-color: Aqua;
opacity: 0.3;
}
#imgPtr {
position: absolute;
background-color: red;
width: 1px;
height: 1px;
border-radius: 50%;
border: 2px solid red;
}
a {
color: red;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://cdn.rawgit.com/konvajs/konva/1.6.5/konva.min.js"></script>
<p>
<span id='text'>How crop rect relates to original image. First image is the Konva stage, second is the original image. Use click and drag to draw successive rects on the Konva image.</span> <a id='reset'>Reset</a>
</p>
<p>
<span id='imgInfo'></span><br />
<span id='imageInfo'></span>
<span id='rectInfo'>Rect info </span><br/>
<span id='posInfo'>Pos on stage </span><br/>
<span id='scaleInfo'>Scale 1:1 </span>
</p>
<div id='container' class='container'></div>
<div id='container2' class='container'>
<img id='daImg' />
<div class='imgRect'></div>
<div id='imgPtr'></div>
</div>
我正在使用 KonvaJS 创建一个简单的图像编辑器。作为图像调整大小功能的基础,我使用了 Konva 示例 (Knonva JS Image Resize) 中的代码。但是现在我正在努力实现裁剪功能。单击按钮时,我允许用户在舞台上绘制一个矩形。然后,我使用图像上的内置裁剪功能以及所绘制矩形的坐标、宽度和高度来裁剪图像。但是当我在裁剪图像之前调整图像大小时,裁剪区域显示从原始尺寸图像裁剪的部分。
是否有一种简单的构建方式让我能够从调整大小的图像中裁剪?或者我是否必须根据图像调整后的值计算绘制矩形的位置和大小,然后裁剪该部分并调整大小并重新定位结果?
剪裁图片:
function cropImage(x, y, width, height, activeLayer) {
var image = activeLayer.get('Image')[0], xDiff = 0, yDiff = 0, newWidth, newHeight, newX, newY;
// only Crop visible Parts of the Image
if(x < activeLayer.getX()) {
xDiff = activeLayer.getX() - x;
}
if(y < activeLayer.getY()) {
yDiff = activeLayer.getY() - y;
}
if (x + width > activeLayer.getX() + activeLayer.width()) {
width = width - ((x + width) - (activeLayer.getX() + activeLayer.width()));
}
if (y + height > activeLayer.getY() + activeLayer.height()) {
height = height - ((y + height) - (activeLayer.getY() + activeLayer.height()));
}
newHeight = height - yDiff;
newWidth = width - xDiff;
newX = (x - activeLayer.getX()) + image.cropX() + xDiff;
newY = (y - activeLayer.getY()) + image.cropY() + yDiff;
image.width(newWidth);
image.height(newHeight);
activeLayer.width(newWidth);
activeLayer.height(newHeight);
activeLayer.setX(newX + activeLayer.getX() - image.cropX());
activeLayer.setY(newY + activeLayer.getY() - image.cropY());
image.crop({
x : newX ,
y : newY ,
width : newWidth,
height : newHeight
});
//Reposition anchors so topLeft Anchor is always in 0/0 of the grouplayer
repositionAnchors(activeLayer);
activeLayer.draw();
}
调整大小时,我在数组中保留宽度和高度的比率,并计算为裁剪绘制的矩形的位置和尺寸:
cropX = ((x / ratios[id].width) - (activeLayer.getX() / ratios[id].width)) + (image.cropX()) + (xDiff / ratios[id].width);
cropY = ((y / ratios[id].height)- (activeLayer.getY() / ratios[id].height)) + (image.cropY()) + (yDiff / ratios[id].height);
cropWidth = (width / ratios[id].width) - (xDiff / ratios[id].width),
cropHeight = (height / ratios[id].height) - (yDiff / ratios[id].height)
这还不是您问题的准确答案,但我将其包含在这里是因为它可能会为您或处于同一种植区的其他人指明方向。
运行 片段全屏。
左图为Konva,右图为原图。
在左侧图像上单击并拖动以进行选择。左侧图像仅更改为选择,而右侧显示裁剪位置。重复这个过程,看看庄稼是如何生长的。
在此示例中,我没有缩放 canvas 上的图像,因此它不能准确回答您目前的需求,但有助于可视化正在发生的事情。如果人们认为它有用,我可以添加缩放。
// Useful frequently used variables.
var sX = 0, sY = 0, sW = 400, sH = 200; // drawing dimensions
var iW = 0, iH = 0; // image dimensions
var cropRect = {x: sX, y: sY, width: iW, height: iH}; // scaled rect
var imgRect = $('.imgRect');
var imgPtr = $('#imgPtr');
var scale = 1;
var img = $('#daImg');
var src = "https://dummyimage.com/400x200/e85de8/fff&text=SO Rocks!"
$('.container').css({width: sW, height: sH});
// Vars for mouse rect work.
var posStart, posNow, mode = '';
// Set up add a stage & layer
var s1 = new Konva.Stage({container: 'container', width: sW, height: sH});
var l1 = new Konva.Layer({});
s1.add(l1);
var image = new Konva.Image({}) // prepare an image to display the picture.
l1.add(image);
// I use a foreground rect to catch events - this covers the konva image completely - you can wire your events in your own way
var r1 = new Konva.Rect({x: 0, y: 0, width: sW, height: sH, fill: 'gold', opacity: 0 })
l1.add(r1)
// draw a rectangle to be used as the rubber-band area
var r2 = new Konva.Rect({x: 0, y: 0, width: 0, height: 0, stroke: 'red', dash: [2,2]})
r2.listening(false); // stop r2 catching our mouse events otherwise if we reverse mouse direction events may not fire
l1.add(r2)
// Mouse movement funcs
function startDrag(posIn){
posStart = {x: posIn.x, y: posIn.y};
posNow = {x: posIn.x, y: posIn.y};
}
// update rubber rect position
function updateDrag(posIn){
posNow = {x: posIn.x, y: posIn.y};
var posRect = reverse(posStart,posNow);
r2.x(posRect.x1);
r2.y(posRect.y1);
r2.width(posRect.x2 - posRect.x1);
r2.height(posRect.y2 - posRect.y1);
r2.visible(true);
s1.draw(); // redraw any changes.
sayRect(r2);
showImgRect(r2);
}
// start the rubber rect drawing on mouse down.
r1.on('mousedown', function(e){
mode = 'drawing';
startDrag({x: e.evt.layerX, y: e.evt.layerY})
})
// update the rubber rect on mouse move - note use of 'mode' var to avoid drawing after mouse released.
r1.on('mousemove', function(e){
if (mode === 'drawing'){
updateDrag({x: e.evt.layerX, y: e.evt.layerY})
}
showImgPtr(e.evt.layerX, e.evt.layerY);
sayPos(e.evt.layerX, e.evt.layerY);
})
// When user releases the mouse we note the size and modify the clip rect.
r1.on('mouseup', function(e){
mode = '';
r2.visible(false);
// leave a rect to show the target
imgRect.hide();
var imgRect2 = imgRect.clone();
imgRect2
.appendTo('#container2')
.addClass('deleteMe')
.show();
setCrop(r2);
sayInfo(img, image);
})
// Draw a rect on the original image to show location and size. Just using some simple jquery to manipulate a div.
function showImgRect(r){
imgRect.css({
left: r.x() + cropRect.x,
top: r.y() + cropRect.y,
width: r.width() * 1,
height: r.height() * 1
})
imgRect.show();
}
// show a mouse pointer on the original image so we get a sense of what is going on
function showImgPtr(x, y){
imgPtr.css({ left: cropRect.x + x, top: cropRect.y + y})
}
// Set the new crop rect, taking account of previous crops
function setCrop(r){
image.cropX(r.x() + cropRect.x);
image.cropY(r.y() + cropRect.y);
image.cropWidth(r.width() * scale);
image.cropHeight(r.height() * scale);
image.width(r.width());
image.height(r.height());
l1.draw();
cropRect = {x: cropRect.x + r.x(), y: cropRect.y + r.y(), width: r.width(), height: r.height()};
}
// This event listener is fired when the image is loaded - could be a few secs delay for a big image
// so this is effectively an async technique.
img.on('load', function() {
// note the dimensions
iW = img.width();
iH = img.height();
// set the konva image details
image.x(sX);
image.y(sY);
image.width(iW);
image.height(iH);
image.image(img[0]);
sayInfo(img, image);
l1.draw(); // redraw the layer to see what happened
});
// This innocent looking line intiates the image load and ultimately fires the event above.
img.prop('src', src);
/*
From here down is utility stuff
*/
// Say something useful
function sayInfo(img, image){
$('#imgInfo').html("HTML Image size " + img.width() + " x " + img.height());
$('#imageInfo').html("Konva.image " + image.x() + ", " + image.y() + " - " + image.width() + " x " + image.height());
var info = $('#info');
}
function sayRect(r){
var rectInfo = $('#rectInfo');
rectInfo.html("Clip rect on canvas " + r.x() + ", " + r.y() + " - " + r.width() + " x " + r.height());
}
function sayPos(x, y){
var posInfo = $('#posInfo');
posInfo.html("Pos on stage " + x + ", " + y);
}
// This is just to reverse co-ords if user drags left / up
function reverse(r1, r2){
var r1x = r1.x, r1y = r1.y, r2x = r2.x, r2y = r2.y, d;
if (r1x > r2x ){
d = Math.abs(r1x - r2x);
r1x = r2x; r2x = r1x + d;
}
if (r1y > r2y ){
d = Math.abs(r1y - r2y);
r1y = r2y; r2y = r1y + d;
}
return ({x1: r1x, y1: r1y, x2: r2x, y2: r2y}); // return the corrected rect.
}
// reset function
function reset(){
sX = 0; sY = 0; sW = 400; sH = 300; // drawing dimensions
iW = 0; iH = 0; // image dimensions
iW = img.width();
iH = img.height();
cropRect = {x: sX, y: sY, width: iW, height: iH}; // scaled rect
scale = 1;
if (image){
console.log('iH=' +iH);
image.x(sX);
image.y(sY);
image.width(iW);
image.height(iH);
image.cropX(sX);
image.cropY(sY);
image.cropWidth(iW);
image.cropHeight(iH);
}
$('.deleteMe').remove();
$('.imgRect').hide();
l1.draw();
}
$('#reset').on('click', function(){reset()});
p
{
padding: 5px;
}
.container {
position: relative;
display: inline-block;
width: 500px;
height: 400px;
background-color: transparent;
overflow: hidden;
border: 1px solid silver;
}
.imgRect {
position: absolute;
border: 1px dotted red;
background-color: Aqua;
opacity: 0.3;
}
#imgPtr {
position: absolute;
background-color: red;
width: 1px;
height: 1px;
border-radius: 50%;
border: 2px solid red;
}
a {
color: red;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://cdn.rawgit.com/konvajs/konva/1.6.5/konva.min.js"></script>
<p>
<span id='text'>How crop rect relates to original image. First image is the Konva stage, second is the original image. Use click and drag to draw successive rects on the Konva image.</span> <a id='reset'>Reset</a>
</p>
<p>
<span id='imgInfo'></span><br />
<span id='imageInfo'></span>
<span id='rectInfo'>Rect info </span><br/>
<span id='posInfo'>Pos on stage </span><br/>
<span id='scaleInfo'>Scale 1:1 </span>
</p>
<div id='container' class='container'></div>
<div id='container2' class='container'>
<img id='daImg' />
<div class='imgRect'></div>
<div id='imgPtr'></div>
</div>