Why is an image distorted when rotated 90/180deg? - javascript

I'am using the CropperJs library to crop and edit an image in an ionic application. I need to resize the image freely using the entire screen as cropbox, So, I had to set the crop box size equal to the width of the device and the remaining height of the container in order.
Here's the instantiation:
const offsetHeight = this.shapeContainer.nativeElement.offsetHeight;
const offsetWidth = this.shapeContainer.nativeElement.offsetWidth
this.cropper = new Cropper(this.image.nativeElement, {
dragMode: "move",
aspectRatio: offsetWidth / offsetHeight,
minCropBoxWidth: offsetWidth,
minCropBoxHeight: offsetHeight,
viewMode: 0,
restore: false,
guides: false,
center: false,
highlight: false,
cropBoxMovable: false,
cropBoxResizable: false,
toggleDragModeOnDblclick: false,
modal: false,
rotatable: true,
zoomable: true,
});
Export in Base64 :
this.cropper
.getCroppedCanvas({
width: this.shapeContainer.nativeElement.offsetWidth,
height: this.shapeContainer.nativeElement.offsetHeight,
maxHeight: this.shapeContainer.nativeElement.offsetHeight * 4,
maxWidth: this.shapeContainer.nativeElement.offsetWidth * 4,
fillColor: '#000',
imageSmoothingQuality: 'high'
})
.toDataURL("image/jpeg");
HTML:
<div class="image-container" #shapeContainer>
<img [src]="imageBase64" #image alt="">
</div>
SASS:
.image-container {
flex: 1;
position: relative;
height: 80vh;
img {
width: 100%;
height: auto;
display: block;
max-width: 100%;
}
}
When I keep the orientation of the image as the first upload it works great. Here's an example:
The issue comes when i call
this.cropper.rotate(90)
Here's the image that I actually pushed til the edge as the previous one:
I honestly don't know what is going on. I tried everything i could, setting different aspect ratio once the rotate method gets called, changing some options passed through the Cropper class but nothing seems to work. I don't get either if this is a cropperJS bug, even though I don't quite understand why an image in a fixed container with fixed aspect ratio behaves like that.
Does anyone know what's happening?

Related

How can I change dynamically the Width and Height to my Lottie in React?

I'm working on a web app builded in Django and React, I've a question about to change the width and height of my Lottie img when the screen change Its size.
That's my Lottie configuration:
const defaultOptions = {
loop: true,
autoplay: true,
animationData: animation,
rendererSettings: {
preserveAspectRatio: "xMidYMid slice"
}
}
<div id='lottie-container'>
<Lottie
id='lottie-icon'
options={defaultOptions}
/>
</div>
That's CSS media query:
/*------|
Desktop |
-------*/
#media screen and (min-width:991px) {
/* LOTTIE */
#lottie-icon{
width: 400px;
height: 400px;
}
}
/*-----|
Mobile |
------*/
#media screen and (max-width:991px) {
/* LOTTIE */
#lottie-icon{
width: 200px;
height: 200px;
}
}
Your issue here seems to be the way you assign an id to your Lottie component.
Here is how you can assign a class / id to your generated svg element (according to this):
const defaultOptions = {
loop: true,
autoplay: true,
animationData: animation,
rendererSettings: {
preserveAspectRatio: "xMidYMid slice",
className: "lottie-svg-class",
id: "lottie-svg-id"
}
}
Then you can manipulate its size in your CSS, either by using media queries or using vw / vh.
You will need to add !important tags though in order to override the style properties of the svg:
.lottie-svg-class{
width:calc(max(500px, 40vw))!important;
}
Try to add only to your media query:
#media only screen and (max-width: 600px) {
}
you can also try to use vw and vh or percentage instead of px. I usually tend to use them for responsiveness.

when loading swiper slide, height changes

I'm initializing swiper with below code:
var mySwiper = new Swiper('.swiper-container', {
speed: 400,
slidesPerView: 3,
autoplay: {
delay: 3000,
},
});
But when the page loads it shows big blue space followed by slides which adds to Layout shift in Google Pagespeed score. Can anybody point out whats wrong?
E D I T:
Just select the swiper default wrapper and set it's height & width properties to whatever size you wish like this:
.swiper-wrapper {
max-height: 100vh;
height: 100vw;
}
By default this should do it, you might also want check if you didn't set specific dimensions on single slides in your css!

Swiper - How center slides if the total slides width fit in the container width

"swiper": "^4.2.6"
Swiper demo
Swiper repository
Hello,
I have this slider setup:
const options = {
wrapperClass: 'slides-list',
slideClass: 'slide',
centeredSlides: true,
slidesPerView: 'auto', // Default slides per view
spaceBetween: 20,
loop: false,
navigation: {
prevEl: '.slider_btn--prev',
nextEl: '.slider_btn--next'
},
pagination: {
el: '.slider_pagination',
clickable: true,
},
speed: 300,
on: {
init: function () {
console.log('swiper initialized');
},
}
};
So i have this setup. Black border for the slider wrapper, red border for the slides list.
Works fine on mobile
But lets face the moment when all the slides width can fit into the slider wrapper, how can i centered then and maybe disable the drag option.
I dont know the slides widths, so right now in the example i have just 3 slides and this is the current behavior
And this one would be the desired one
I cannot use media queries because i dont know when the slides gonna fit in the container. so, Swiper has someway to achieve this ?
Thanks in advance.
I dont want to use slidesPerView: 3, as i said, the point is to be
able to check all the slides that we have, then if all of them can fit
in the container, center them. Not to force X number of slides into
the view
Javascript
const slider = new Swiper(element, {
slidesPerView: 'auto',
watchOverflow: true
});
slider.on('resize', function (instance) {
element.classList.toggle('.slider-active', instance.virtualSize > instance.size);
});
CSS
.swiper-wrapper {
justify-content: center;
}
.slider-active .swiper-wrapper {
justify-content: normal;
}
This way you apply css to center the slides when te slide wrapper does not overflow the container. And visa versa to reset

How to avoid transparent background using cropping plugin

I'm using this cropping tool https://github.com/fengyuanchen/cropper/. I have this issue, that if I add an image dynamically there is some transparent background around the image. So the image does not fit the container and it also makes it possible to crop around the image. I followed the examples on the docs to try to get rid of the transparent background, but with no success.
here is my code:
<div id="imgWrap" style="max-height:400px;min-height:400px">
<img id="img" src="" /> // Image gets added dynamically
</div>
the javascript
var reader = new FileReader();
reader.onloadend = function () {
var img = $('#imgWrap img');
img.attr('src', reader.result);
img.cropper({
aspectRatio: 1 / 1,
autoCropArea: 0.65,
guides: false,
strict: true,
highlight: false,
responsive:true,
dragCrop: false,
movable: true,
resizable: true,
zoomable: true,
touchDragZoom:true,
rotatable: false,
minCropBoxWidth:105,
minCropBoxHeight:105,
built: function () {
// cropper-container is the element where the image is placed
$('.cropper-container').cropper('setCanvasData', {
left: 0,
top: 0,
width: 700,
height: 700
}
);
},
})
I tried to this: https://github.com/fengyuanchen/cropper#setcanvasdatadata but nothing happens
You can see an example here:
The natural size of the image is 1920x1200
This is what is generated after the image is added:
So, does anyone have a suggestion how to get rid of the transparent background and make the image fit the container?
I had the exact same issue. In the Cropper doc it says to set the img max-width = 100%. I did this and it fixed the issue
https://github.com/fengyuanchen/cropper
/* Limit image width to avoid overflow the container */
img {
max-width: 100%; /* This rule is very important, please do not ignore this! */
}
Setting background property of cropper object to false fixes this problem.
You can set option:
aspectRatio: 1 / 1, // select area ratio 16:9, 4:3, 1:1, 2:3, free
viewMode: 3, // sketch image to fit the container
In case someone else gets a similar problem, I fixed mine by encasing the <img> its in own div. Cropper (at least in 2.0.1) defines the container with
$cropper.css((this.container = {
width: max($container.width(), num(options.minContainerWidth) || 200),
height: max($container.height(), num(options.minContainerHeight) || 100)
}));
and $container is created with this.$container = $this.parent(); so if you have padding, some other lines of code, etc it calculates its size along with those lines. Given the age of this though, I doubt OP can validate if that was his problem or not.
I had a same problem and solution was easy.
Everything what you need is setup your css height, width to your cropper selector instead of cropper but after init cropper. This is normal jQuery object and you call cropper init on him later. As latest thing you'll setup new visual variables.
var $area = $('div.crop-area-image'); // jquery object
$area.cropper(options); // init cropper
$area.css({height: '300px'}); // setup css
voala .. thats all!
Unfortunatelly
/* Limit image width to avoid overflow the container */
img {
max-width: 100%; /* This rule is very important, please do not ignore this! */
}
is not enough. This only fixes top and bottom empty space issue.
I had to add display: inline-block; to my container to clamp canvas and image boxes: https://jsfiddle.net/h9ktgxak/
Use fillColor option in the getCroppedCanvas method
Also, make sure to use full color name ('#ffffff') not ('#fff')
getCroppedCanvas({fillColor:'#ffffff'}).toBlob((blob) => {});
You call setCanvasData method on wrong element.
You should call it on the image:
...
img.cropper({
...
built: function () {
img.cropper('setCanvasData', {
left: 0,
top: 0,
width: 700,
height: 700
});
}
});
...

How to determine where the current visible vertical location inside a jquery dialog is?

I have a case where I am using a jquery ui dialog and I have any html table in the dialog where the dialog is fixed height:
$("#modalDialogContainer").dialog({
resizable: false,
height: 700,
autoOpen: false,
width: 1050,
modal: true,
I call an AJAX query from a button click and I want to use jquery UI blockUI plugin to show a "loading" message. Something like this:
$("#myTableInsideDialog").block({
css: {
top: '200px',
bottom: "",
left: ''
},
centerY: false, baseZ: 2000, message: $("#SavingMessage")
});
The issue I have is that the content in the dialog is longer than the height of the dialog
and I given the dialog is FIXED height so that causes the dialog to have a vertical scroll bar.
Having the scroll bar is fine (that's actually what I want) but the knock on effect is that
because of that depending if the user has scrolled down or not, the blockUI message is not centered (or even visible on the screen) vertically.
Question: Is there anyway I can detect what is visible areas inside a dialog that has a vertical scroll bar to vertically align the block message properly?
Above as you can see its hard coded to be 200px from the top so it works great if the user hasn't scrolled down but you can't see the message if the user has scrolled down the whole way
In short, if i am at the top of the scroll, then i would have this:
$("#myTableInsideDialog").block({
css: {
top: '200px',
bottom: "",
left: ''
},
centerY: false, baseZ: 2000, message: $("#SavingMessage")
});
if i am at the bottom of the scroll, then i would want this:
$("#myTableInsideDialog").block({
css: {
top: '',
bottom: "200px",
left: ''
},
centerY: false, baseZ: 2000, message: $("#SavingMessage")
});
I wouldn't alternate between top AND bottom properties:
For a window sized 1000px, top:800 == bottom:200
The important question, is how you can find out your scroll distance from the top. For that lets use a function:
function calcTopLocal() {
var s = $('#modalDialogContainer').scrollTop() + 'px';
return s;
}
Now, to apply it to your block:
$("#myTableInsideDialog").block({
css: {
top: calcTopLocal()
},
centerY: false, baseZ: 2000, message: $("#SavingMessage")
});
This can be refactored many ways. The significant detail is using scrollTop() and applying styling.
response to MKaama:
My proposed answer has no loops, no timers, and no suggestions of repeated action. There is no
Repeatedly calling a js function just to keep the position fixed is an overkill, a waste of CPU
If you want to add an loading message when the ajax is requesting the data, you can append a <div> on the dialog containing the message you want to display. Then you can apply a relative position to the dialog and an absolute position to the <div> and with margin:auto the div remains in the center of dialog always, even if you scroll the dialog.
jsFiddle demo
$("#modalDialogContainer").dialog({
resizable: true,
height: 300,
autoOpen: true,
width: 300,
modal: true,
buttons: {
'call ajax': function(){
// insert the loading div to the dialog
$(this).parent().append("<div class='loading' />");
$.ajax({
type: 'json',
url: 'jsonRequest.php',
complete: function(){
// remove the loading div
$('.loading').remove();
},
success: function(){
//do what you want
}
});
}
}
});
the CSS file should be something like this
#modalDialogContainer{
position: relative;
}
#myTableInsideDialog{
height: 1000px;
width: 100%;
}
.loading{
position: absolute;
top: 0px;
bottom: 0px;
left: 0px;
right: 0px;
margin: auto;
...
}
there is a useful plugin that can tell if an element is visile on screen or not ( scrolled to ) , you may simply use it , the function returns true for visible areas on screen :
Here is a quick demo:
http://opensource.teamdf.com/visible/examples/demo-basic.html
Here is the source page :
http://www.teamdf.com/web/194/jquery-element-onscreen-visibility
usage as simple as:
$('#element').visible()
Use
$('#modalDialogContainer').scrollTop()
to find the amount of user's scroll.
You can then show your message with
{ top: $('#modalDialogContainer').scrollTop()+'px' }
And it will always be visible for them, and appear at the top of what they are looking at :)
Why bother with the height of the content at all?
I mean, isn't an easier solution to the problem possible by putting a "BlockUI" on the JQuery Dialog. Since you have a fixed height there, your block UI would most certainly be fixed as well. There is no way the scroll can now affect your message.
A crude example is hosted here in fiddle. It gives you both experiences so you can see how it behaves.
For example, you can put the block UI on the following class.
var container = ".ui-dialog";
$(container).block({
message: '<h1>Processing</h1>'
});
$.ajax({
url: "/echo/json/",
data: {
json: {},
delay: 5
}
}).done(function() {
console.log("Done with ajax");
$(container).unblock();
});

Categories

Resources