Reputation: 213
I am trying to create a stage using konva.js. Basically, I need to add two (at least) or more images on the stage based on some layouts.
Example:
I have a layout that splits the stage area verically into two similar groups. Each group has one image.
Example code:
var stage = new Konva.Stage({
container: 'container',
width: width, // 295px
height: height, // 600px
});
var layer = new Konva.Layer({
imageSmoothingEnabled: true
});
// add a vertical line in order to show seperated groups
var line = new Konva.Line({
points: [stage.width() / 2, 0, stage.width() / 2, stage.height()],
stroke: '#9499a3',
strokeWidth: 2,
lineCap: 'round',
lineJoin: 'round',
});
layer.add(line)
// create group #1
var group1 = new Konva.Group({
x: 0,
y: 0,
width: stage.width() / 2,
height: stage.height()
});
var image1;
var imageObj = new Image();
imageObj.onload = function () {
image1 = new Konva.Image({
x: (stage.width() / 2 - imageObj.width) - line.strokeWidth() / 2,
//y: 0,
width: imageObj.width,
height: stage.height(),
image: imageObj,
draggable: true,
});
//layer.add(image1);
group1.add(image1);
image1.on('dragstart', function () {
console.log('dragstart')
});
image1.on('dragmove', function(e){
console.log('X : ' + this.attrs.x + ', Y : ' + this.attrs.y)
});
image1.on('dragend', function () {
console.log('X : ' + this.attrs.x + ', Y : ' + this.attrs.y)
});
};
imageObj.src = 'img/1.jpg';
// create group #2
var group2 = new Konva.Group({
x: stage.width() / 2 + line.strokeWidth() / 2,
y: 0,
width: stage.width() / 2,
height: stage.height()
});
var image2;
var imageObj = new Image();
imageObj.onload = function () {
image2 = new Konva.Image({
//x: stage.width() / 2 + line.strokeWidth() / 2,
//y: stage.height() / 2 - imageObj.height / 2,
width: imageObj.width,
height: stage.height(),
image: imageObj,
draggable: true,
});
//layer.add(image2);
group2.add(image2);
};
imageObj.src = 'img/2.jpg';
layer.add(group1, group2)
stage.add(layer);
What I want to do:
Upvotes: 2
Views: 1588
Reputation: 9525
So the way to achieve this is to have a rect defining the frame in which the image will be constrained. Put this into a group and set the clip region of the group to match the position and size of the frame rect. Now add the image to the group. Only the part of the image in the group will be visible.
As a bonus, if you add a dragBoundFunc to the group you can ensure that an oversized image cannot be dragged beyond frame edges.
See snippet below (best run full-screen) and editable CodePen here.
This is, of course, only a single image frame where you have described that you will have say two in your use case. I suggest that you unravel the code then make a class, then you can use as many as required, etc.
// this data gives the position and size of the frame
let data = {
frameGroup: { x: 50, y: 100, width: 800, height: 300, strokeWidth: 10, stroke: 'cyan'},
fadeImage: {opacity: 0.3}
}
// add a stage
let stage = new Konva.Stage({container: 'container', width: 1000, height: 500 }),
layer = new Konva.Layer({}), // Add a layer and group to draw on
group = new Konva.Group({clip: data.frameGroup}),
rect = new Konva.Rect(data.frameGroup),
image = new Konva.Image({draggable: true}),
fadeImage = null,
imageObj = new Image();
stage.add(layer);
layer.add(group)
group.add(rect);
rect.listening(false); // stop the frame rect intercepting events
// Use the html image object to load the image and handle when laoded.
imageObj.onload=function () {
image.image(imageObj); // set the Konva image content to the html image content
// compute image position so that it is initially centered in the frame
let imagePos = getMiddlePos(data.frameGroup, data.frameGroup, {width: imageObj.width, height: imageObj.height});
// set the Konva image attributes as needed
image.setAttrs({
x: imagePos.x, y: imagePos.y, width: imageObj.width, height: imageObj.height,
// This function ensures the oversized image cannot be dragged beyond frame edges.
// Is is firect by the drag event.
dragBoundFunc: function(pos){
var imagePos = this.getClientRect(); // get the image dimensions.
let
maxPos = { // compute max x & y position allowed for image
x: data.frameGroup.x,
y: data.frameGroup.y
},
minPos = {
x: data.frameGroup.x + data.frameGroup.width - imagePos.width,
y: data.frameGroup.y + data.frameGroup.height - imagePos.height
},
newX = (pos.x >= maxPos.x) ? maxPos.x : pos.x, // ensure left edge not within frame
newY = (pos.y >= maxPos.y) ? maxPos.y : pos.y; // ensure top edge not within frame
newX = newX < minPos.x ? minPos.x : newX; // ensure right edge not within frame
newY = newY < minPos.y ? minPos.y : newY; // ensure top edge not within frame
fadeImage.setAttrs({x: newX, y: newY}); // apply what we computed
// dragBoundFunc must return a value with x & y. Either return same value passed in
// or modify the value.
return {
x: newX,
y: newY
};
}
})
group.add(image) // add the image to the frame group
image.moveToBottom(); // ensure the frame rect is above the image in the z-index.
// make a clone of the image to be used as the fade image.
fadeImage = image.clone({draggable: false, opacity: data.fadeImage.opacity});
layer.add(fadeImage);
// ensure fade image is one step below the frame group. ! - in this simple demo Konva will raise
// a warning because group.zIndex() = 0.
fadeImage.zIndex(group.zIndex() - 1);
}
imageObj.src = "https://assets.codepen.io/255591/hubble_space_image.jpg?x=1"
// simple function to get the x & y for the image to be centered in the frame
function getMiddlePos(framePos, frameSize, imageSize){
return{
x: framePos.x + (frameSize.width - imageSize.width)/2,
y: framePos.y + (frameSize.height - imageSize.height)/2,
}
}
// Toggle use of fade image to show overflowed part of image.
$('#useFadImage').on('change', function(e){
if (fadeImage){
fadeImage.visible(!fadeImage.visible());
}
})
body {
margin: 14px;
padding: 10px;
font: 12pt Verdana, Arial, sans-serif;
}
#container {
width: 1000px;
height: 500px;
border: 1px solid red;
}
<p><h2>Constrain an image to the bounds of a frame</h2></p>
<p>1. Set the frame group clip region.</p>
<p>2. Set dragBounds function on the image so that it cannot escape the group.</p>
<p>Drag the image and note that it cannot me dragged such that white space in the frame would be visible.</p>
<p>
<input type='checkbox' id='useFadImage' checked='' /><label for="useFadImage">Use fade image - shows area of image outside the frame </label>
</p>
<div id='container' ></div>
<p>Image from <a href="https://www.nasa.gov/multimedia/imagegallery/iotd.html">NASA Image of the day.</a></p>
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js'></script>
<script src="https://unpkg.com/konva@8/konva.min.js"></script>
There is a blog entry on the subject here.
Upvotes: 4