Image magnifier in Angular - javascript

I am trying to implement a image maginfier on hover.I tried to replicate the code as in w3schools which is purely of Javascript.I am trying to implement the following code in Angular
https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_image_magnifier_glass
I used the above method in typescript and called it from ngOnInit in Angular but i am not able to get any result from the method.I have ensured the id is passed correctly and validated the method is being called .But still not able to get any result .I wish not to use any npm packages for magnifier since most of them had bugs.
component.ts
ngOnInit(){
this.magnify(imgID, zoom)
}
magnify(imgID, zoom) {
var img, glass, w, h, bw;
img = document.getElementById(imgID);
/*create magnifier glass:*/
glass = document.createElement("DIV");
glass.setAttribute("class", "img-magnifier-glass");
/*insert magnifier glass:*/
img.parentElement.insertBefore(glass, img);
/*set background properties for the magnifier glass:*/
glass.style.backgroundImage = "url('" + img.src + "')";
glass.style.backgroundRepeat = "no-repeat";
glass.style.backgroundSize = (img.width * zoom) + "px " + (img.height * zoom) + "px";
bw = 3;
w = glass.offsetWidth / 2;
h = glass.offsetHeight / 2;
/*execute a function when someone moves the magnifier glass over the image:*/
glass.addEventListener("mousemove", moveMagnifier);
img.addEventListener("mousemove", moveMagnifier);
/*and also for touch screens:*/
glass.addEventListener("touchmove", moveMagnifier);
img.addEventListener("touchmove", moveMagnifier);
function moveMagnifier(e) {
var pos, x, y;
/*prevent any other actions that may occur when moving over the image*/
e.preventDefault();
/*get the cursor's x and y positions:*/
pos = getCursorPos(e);
x = pos.x;
y = pos.y;
/*prevent the magnifier glass from being positioned outside the image:*/
if (x > img.width - (w / zoom)) {x = img.width - (w / zoom);}
if (x < w / zoom) {x = w / zoom;}
if (y > img.height - (h / zoom)) {y = img.height - (h / zoom);}
if (y < h / zoom) {y = h / zoom;}
/*set the position of the magnifier glass:*/
glass.style.left = (x - w) + "px";
glass.style.top = (y - h) + "px";
/*display what the magnifier glass "sees":*/
glass.style.backgroundPosition = "-" + ((x * zoom) - w + bw) + "px -" + ((y * zoom) - h + bw) + "px";
}
function getCursorPos(e) {
var a, x = 0, y = 0;
e = e || window.event;
/*get the x and y positions of the image:*/
a = img.getBoundingClientRect();
/*calculate the cursor's x and y coordinates, relative to the image:*/
x = e.pageX - a.left;
y = e.pageY - a.top;
/*consider any page scrolling:*/
x = x - window.pageXOffset;
y = y - window.pageYOffset;
return {x : x, y : y};
}
}

Here is a working stackblitz as per your requirements. It shows the implementation of image zoom functionality mentioned on w3school.
https://stackblitz.com/edit/angular-w3school-image-magnification
You have not shown your html and css files. So, I am not totally sure but the following might be the reason why zoom is not working for you.
Problem is that img-magnifier-glass div element is created using classical DOM method 'document.createElement'. And then, class 'img-magnifier-glass' is applied to it, again using a classical DOM method (setAttribute). But, in angular, styles are encapsulated. So, if you have added a class definition of '.img-magnifier-glass' in app.component.css then that class won't be applied to glass div since it is not mentioned in the template (app.component.html). See this for more info - https://github.com/angular/angular/issues/7845
To fix this, you can either move definition of class '.img-magnifier-glass' to styles.css (Where global styles are defined)
or you can keep the class in app.component.css but use pseudo-selector ::ng-deep with it. Applying the ::ng-deep pseudo-class to any CSS rule completely disables view-encapsulation for that rule. Any style with ::ng-deep applied becomes a global style.
::ng-deep .img-magnifier-glass {
}
or you can stop style encapsulation for component by specifying
#Component({
// ...
encapsulation: ViewEncapsulation.None, //<<<<< this one!
styles: [
// ...
]
})
It will be better will be if you use Renderer2 (https://angular.io/api/core/Renderer2) instead for creating dynamic elements like glass element here. Renderer2 will take care of correctly encapsulating class applied on elements created using it.

I ran into the same issue but for image zoomer which has the same implementation of magnifier. I got it working by tweaking the css and ts code (which was taken from the magnifier and combined with the zoomer). the answer above is not what I am looking for coz I need the zoomed img to be in a different box and not on top of the img itself. Here is the stackblitz code which I modified to suit what I need.

For implementing image magnification feature like W3School and Amazon in Angular, you can use npm package ng-img-magnifier.
Here is the working DEMO.
<ng-img-magnifier
[thumbImage]='img' [fullImage]='img2'
[top]='top' [right]='right'
[lensWidth]='lensewidth' [lensHeight]='lensheight'
[imgWidth]='imgWidth' [imgHeight]='imgheight'
[resultWidth]='resultWidth' [resultHeight]='resultheight'
>
</ng-img-magnifier>
This package comes with full customization options.
Hopefully this will resolve your issue.

Related

How to load image on page load in Canvas

Hi I am using canvas for drawing but issue is this on page load I want to show an image on canvas so the user can continue from where he left
<div id="wPaint" style="position:relative; width:100%; height:600px; background-color:white; border:1px solid black; resize: both; ">
</div>
canvas is loaded in this div. if I inspect then canvas code is below image
Thanks for any help.
Introduction
The question is referring to a scenario when you have a canvas and the user can change its content. I assume that this works and you need to be able to save the content of the canvas as an image and load that saved image and draw into the canvas the given image.
Exporting the content
var canvas = document.getElementById("mycanvas"); //you can change this to your image
var img = canvas.toDataURL("image/png");
Saving the image
Once you have successfully created the img variable, you can save it. You can store it into localStorage, like
localStorage.setItem('saved', img)
You can store it differently as well, like sending the image to your server in a POST request, but, for the purpose of this question I aim to have a simple solution that works. It would be another task to store this as a file or something of the like in your server.
Loading the image
let img = document.createElement('img');
img.src = localStorage.getItem('saved');
It is possible that img was not saved yet, like in the first load. You will need to properly handle that case, like
if (img.src) {
//do some stuff
}
Putting an image into a canvas
canvas.getContext('2d').drawImage(img, dx, dy);
Hi I create arrow but issue is arrow not perfect on all direction... top , down , right and left direction arrow not perfect
here it image
here it is my code
_drawLineMove: function (a) {
this._drawShapeMove(a, 1);
var b = this.canvasTempLeftOriginal,
c = this.canvasTempTopOriginal;
//var headlen = 10; // length of head in pixels
a.pageX < b && ((a.x = a.x + a.w), (a.w = -1 * a.w)),
a.pageY < c && ((a.y = a.y + a.h), (a.h = -1 * a.h)),
(this.ctxTemp.lineJoin = "round"),
this.ctxTemp.beginPath(),
console.log(a);
const head_len = 16;
const head_angle = Math.PI / 6;
var dx = (a.x + a.w) - a.x;
var dy = (a.y + a.h) - a.y;
const angle = Math.atan2(dy, dx);
this.ctxTemp.beginPath();
this.ctxTemp.moveTo(a.x, a.y), //fromx , fromy
this.ctxTemp.lineTo(a.x + a.w, a.y + a.h), //tox,toy
this.ctxTemp.stroke();
this.ctxTemp.beginPath();
this.ctxTemp.lineTo((a.x + a.w), (a.y + a.h));
this.ctxTemp.lineTo((a.x + a.w) - head_len * Math.cos(angle - head_angle), (a.y + a.h) - head_len * Math.sin(angle - head_angle));
this.ctxTemp.lineTo((a.x + a.w) - head_len * Math.cos(angle + head_angle), (a.y + a.h) - head_len * Math.sin(angle + head_angle));
this.ctxTemp.closePath(),
this.ctxTemp.stroke();
this.ctxTemp.fill();
},
thanks

Resize multiple objects with JS considering rotate

I'm working on visual editor with objects and user interactions around like move, resize, rotate, etc...
I have resize and rotate functionality in place. Now I have implemented multi-select functionality when user select multiple objects and resize objects keeping the original proportion.
That functionality works very well, however not for rotated objects. I've created a simplified codepen example. Basically the question is - how to adjust resize() function to make sure it works well for rotated objects. To reproduce an issue just click on "Rotate" and then "Increase width & height" once or multiple times.
function resize(incrementX, incrementY, offsetX, offsetY) {
...
}
I'm not sure if this is a valid solution for your problem, but you can undo the rotation before resizing, and reset the rotation afterwards. Like this.
function resize(incrementX, incrementY, offsetX, offsetY) {
var old_r = objmultiple.r
rotate(-objmultiple.r)
var ratioX = (objmultiple.w + incrementX) / objmultiple.w;
var ratioY = (objmultiple.h + incrementY) / objmultiple.h;
objmultiple.x += offsetX;
objmultiple.y += offsetY;
objmultiple.w = objmultiple.w + incrementX;
objmultiple.h = objmultiple.h + incrementY;
[obj1, obj2].forEach(function(obj) {
obj.x = (obj.x - objmultiple.x + offsetX) * ratioX + objmultiple.x;
obj.y = (obj.y - objmultiple.y + offsetY) * ratioY + objmultiple.y;
obj.w *= ratioX;
obj.h *= ratioY;
});
rotate(old_r)
}
Codepen here

Find the Points of Intersection of a Circle with a Line in Javascript

I'm trying to animate a given element to go around a pre-defined radius and I'm having trouble getting the position of the element at a Y point given.
I'm trying to find each point with the circle equation, but I can only get one point out of the two possible ones.
In Javascript, I use Math.sqrt( Math.pow(radius, 2) - Math.pow(y, 2) , 2) to get the point. assuming the center of the of the circle is 0,0.
but then I need to translate it to pixels on the screen since there are no negative pixels in positions on the browser.
All the sizing is relative to the window. so the radius, for example, is 80% of the height of the window in my tests.
Also, I'm trying to calculate what the distance of the element between each frame should be for the duration, but I'm not using it yet because I try to fix the issue above first.
This is what I have(a cleaned up version):
let height = window.innerHeight * 0.8,
radius = height / 2,
circumferance = (radius * 2) * Math.PI,
container = document.getElementById('container'),
rotating = document.querySelector('.rotating'),
centerX = radius - (rotating.offsetWidth / 2),
centerY = radius - (rotating.offsetHeight / 2),
duration = 10,
stepDistance = circumferance / 16;
// Setting the dimensions of the container element.
container.style.height = height + 'px';
container.style.width = height + 'px';
// return positive X of any given Y.
function getXOffset(y) {
return Math.sqrt( Math.pow(radius, 2) - Math.pow(y, 2) , 2);
}
// Setting the position of the rotating element to the start.
rotating.style.top = 0 + 'px';
rotating.style.left = centerX + 'px';
setInterval(() => {
let top = parseInt(rotating.style.top),
y = radius - top;
rotating.style.top = (top + 1) + 'px';
rotating.style.left = (centerX + getXOffset(y)) + 'px';
}, 16);
Here is a fiddle with a bit more code for trying to get the right amount of distance between points for a smoother animation(currently needs fixing, but it doesn't bother me yet.)
https://jsfiddle.net/shock/1qcfvr4y/
Last note: I know that there might be other ways to do this with CSS, but I chose to use javascript for learning purposes.
Math.sqrt would only return the positive root. You'll have to account for the negative value based on the application. In this case, you need the positive x value during the 1st half of the cycle and negative during the 2nd half.
To do that, you should implement a method to track the progress and reverse the sign accordingly.
Here is a sample. I modified upon yours.
edit:
Instead of Math.sqrt( Math.pow(radius, 2) - Math.pow(y, 2) , 2) You can use the full formula to get x if you do not want to assume origin as center, which in this case is Math.sqrt( Math.pow(radius, 2) - Math.pow((actualY - centerY), 2) , 2)
explanation:
The original equation (x-a)² + (y'-b)² = r²
becomes x = √(r² - (y'-b)²) + a
Assuming .rotating box have 0 width and height.
The variable equivalents in your code are centerX = a, centerY = b.
By assuming origin as center you're basically doing a pre-calculation so that your y value becomes the equivalent of (y'-b). Hence x = √(r² - y²) + a is valid.
At initial state top = 0
i.e (y'-b) => height - centerY.
In your code y = radius => height/2.
Now (height - centerY) being equal to (height/2) is a side effect of your circle being bound by a square container whose height determines the y value.
In other words, when you use origin as center, you are taking the center offsets outside of circle equation and handling it separately. You could do the same thing by using the whole formula, that is, x = √(r² - (y'-b)²) + a

How to constrain a text element within an square with RaphaelJS?

I'm trying to constrain a text element with custom font within a square. I'm having difficulties to let the constrainment take place.
My code looks like this for the move function:
if (this.attr("y") > offsetY || this.attr("x") > offsetX) { // keep dragging & storing original x and y
this.attr({
x : this.ox + dx,
y : this.oy + dy
});
} else {
nowX = Math.min(offsetX, this.ox + dx);
nowY = Math.min(offsetY, this.oy + dy);
nowX = Math.max(0, nowX);
nowY = Math.max(0, nowY);
this.attr({
x : nowX,
y : nowY
});
}
The constrainment never takes place. However, if I use two squares with this code, it works. What am I overlooking here?
Thanks for your answers :)
If you used the default text-anchor value of 'middle' when you called paper.text(), the x and y attrs will return the coordinates of the center of the text span -- not its upper left corner, as it would with a rect.
Rather than using the x and y attributes, you should get your coordinates via element.getBBox(), and then use the x and y from the resulting object. That should enable your existing logic to work unimpeded.

jQuery image hover effect

I'm trying to achieve this effect with jQuery.
I wrote some of the code, but it's buggy (move to the bottom-right corder and you'll see).
check it out
Basically, if there's an already-built jQuery plugin that you know of that does this, I'd be very happy using it, if not, any help with my formula would be appreciated. This is what I get for not paying attention in Maths classes :)
Thanks in advance.
Maikel
Overall I think this is what you're looking for:
$.fn.sexyImageHover = function() {
var p = this, // parent
i = this.children('img'); // image
i.load(function(){
// get image and parent width/height info
var pw = p.width(),
ph = p.height(),
w = i.width(),
h = i.height();
// check if the image is actually larger than the parent
if (w > pw || h > ph) {
var w_offset = w - pw,
h_offset = h - ph;
// center the image in the view by default
i.css({ 'margin-top':(h_offset / 2) * -1, 'margin-left':(w_offset / 2) * -1 });
p.mousemove(function(e){
var new_x = 0 - w_offset * e.offsetX / w,
new_y = 0 - h_offset * e.offsetY / h;
i.css({ 'margin-top':new_y, 'margin-left':new_x });
});
}
});
}
You can test it here.
Notable changes:
new_x and new_y should be divided by the images height/width, not the container's height/width, which is wider.
this is already a jQuery object in a $.fn.plugin function, no need to wrap it.
i and p were also jQuery objects, no need to keep wrapping them
no need to bind mousemove on mouseenter (which rebinds) the mousemove will only occur when you're inside anyway.
Nick Craver beat me to an answer by about 10 minutes, but this is my code for this, using background-image to position the image instead of an actual image.
var img = $('#outer'),
imgWidth = 1600,
imgHeight = 1200,
eleWidth = img.width(),
eleHeight = img.height(),
offsetX = img.offset().left,
offsetY = img.offset().top,
moveRatioX = imgWidth / eleWidth - 1,
moveRatioY = imgHeight / eleHeight - 1;
img.mousemove(function(e){
var x = imgWidth - ((e.pageX - offsetX) * moveRatioX),
y = imgHeight - ((e.pageY - offsetY) * moveRatioY);
this.style.backgroundPosition = x + 'px ' + y + 'px';
});
The huge amount of variables are there because the mousemove event handler has to be as efficient as possible. It's slightly more restrictive, because you need to know the dimensions, but I think the code can be easily altered to work with imgs for which the size can be calculated easily.
A simple demo of this: http://www.jsfiddle.net/yijiang/fq2te/1/

Categories