Reputation: 439
Is there a way to keep image proportion when I upload image to konva.js. Basically, I am using konva with Vue extension and I need to have such a method as uploading background image. I have set the canvas size based on user device and I can make the uploading image size to that canvas dimensions, but it will not save it's proportion.
One way I think, I can do it by writing function to determine the max height or width (choose which is more suitable in particular case) the image can achieve and then resize it proportionally, but I don't see this answer as elegant and free from bugs, so I thought that I will ask before doing it.
Upvotes: 0
Views: 637
Reputation: 9535
You will need to handle the image scaling yourself but it is not difficult.
As you have identified, you need a function to set the scale of the image to cover the viewport whilst retaining the aspect ratio.
The math needed is to compute the ratio of viewport width to image width, and same for height. Then compare the two ratios and use the larger.
Unless the viewport and image are exactly the same aspect ratios, you will find some of the image is clipped as can be seen in the snippet when selecting the 300x500 button.
The snippet below shows the pink rectangle which represents a viewport. The dimension buttons change the viewport proportions, and the width x height buttons call in different images. Using the buttons in comnination you can see how the fitting function works.
Experiment 1: On start, click the 'wider' button repeatedly and see how portions of the top and bottom of the image are clipped outside the viewport.
Experiment 2: On start, click 'narrower' repeatedly and observe similar effect.
/*
function to caluclate and return appropriate scale to fill one rect with another whilst preserving aspect ratio
*/
var autoScale = function(container, imgEle){
var rW = container.width() / imgEle.width;
var rH = container.height() / imgEle.height;
var scale = (rW < rH ? rH : rW);
return {x: scale, y: scale};
}
// from here on the code is about making the demo.
var sz = '600x600';
var miniMag = 0.3333
// Set up the canvas / stage
var div = $('#container');
var stage = new Konva.Stage({container: 'container', width: div.width(), height: div.height()});
var layer = new Konva.Layer({draggable: false});
stage.add(layer)
stage.scale({x: miniMag, y: miniMag});
var pic = new Konva.Image({ x: 300, y: 300});
layer.add(pic);
var rect = new Konva.Rect({x: 300, y: 300, width : div.width(), height: div.height(), stroke: 'magenta'})
layer.add(rect)
stage.draw()
// load an image
var imageObj = new Image();
imageObj.onload = function(){
pic.image(imageObj);
pic.scale(autoScale(rect, imageObj));
pic.x(rect.x() + ((rect.width() - (pic.width() * pic.scaleX()) )/2))
pic.y(rect.y() + ((rect.height() - (pic.height() * pic.scaleY()) )/2))
layer.draw();
}
// if we click a change-size button then change the viewport indicator
$('#narrower').data('change', {x:-10, y: 0});
$('#wider').data('change', {x: 10, y: 0});
$('#shorter').data('change', {x: 0, y: -10});
$('#taller').data('change', {x: 0, y: 10});
$('.btn').on('click', function(e){
var diff = $(this).data('change');
rect.width(rect.width() + diff.x)
rect.height(rect.height() + diff.y)
pic.scale(autoScale(rect, imageObj));
pic.x(rect.x() + ((rect.width() - (pic.width() * pic.scaleX()) )/2))
pic.y(rect.y() + ((rect.height() - (pic.height() * pic.scaleY()) )/2))
layer.draw();
})
// If we click an image selection button change the image
$('.imgsel').on('click', function(e){
imageObj.src = "https://via.placeholder.com/" + $(this).attr('sz');
})
// Kick off with a 600 x 600 image
$('.imgsel600').trigger('click');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.min.js"></script>
<div>
<button id='narrower' class='btn'>Narrower</button>
<button id='wider' class='btn'>Wider</button>
<button id='shorter' class='btn'>Shorter</button>
<button id='taller' class='btn'>Taller</button>
<button class='imgsel' sz='300x300'>300 x 300</button>
<button class='imgsel' sz='300x500'>300 x 500</button>
<button class='imgsel' sz='500x300'>500 x 300</button>
<button class='imgsel imgsel600' sz='600x600'>600 x 600</button>
</div>
<div id='container' style="position: absolute; z-index: -1; display: inline-block; left: 0px; top: 0px; width: 300px; height: 300px; background-color: silver;"></div>
Upvotes: 1