I want to implement the following in HTML/JavaScript but don't really know where to start or even if there is already an existing function in one toolkit for this:
I have one image (e.g. a png) which is visible and a second image of the same size which is not visible. However, if I move the mouse pointer over the first image, the corresponding part from the second image shall be shown around the mouse cursor. So for example, if I move the mouse at position 100,100 on the first image, the section from 50,50 to 150,150 of the second image should be overlaid on the first image at position 50,50 to 150,150. I hope this is understandable.
Does anyone know, if there is already a library which contains this functionality? I've already searched for this on the internet but found nothing. However, I do not really know what keywords to search for. So if someone knows a keyword to search for, I would be appreciate hints as well.
Alternatively, Can you give me a cue how could I grep the part of the second image and display it at the mouse position? I was thinking that canvas might be used but I am not sure how to.
Thank you very much and best regards
Tobias
This can be done with Vanilla JS or JQuery. Basic idea behind it is to wrap the image in a container with position:relative and listen to mouse movement on it. A second <div> with position:absolute will receive the coordinates of the mouse pointer with its background position set to match the current mouse offset.
The posted code is just to give an idea how this would look like and needs to be extended to properly handle the edges of the image.
$(".hover-container").on("mousemove", function (e) {
var parentOffset = $(this).parent().offset();
//or $(this).offset(); if you really just want the current element's offset
var relX = e.pageX - parentOffset.left;
var relY = e.pageY - parentOffset.top;
var picHeight = $('.hover-image').height();
var picWidth = $('.hover-image').height();
$('.hover-image')
.css("left", relX - 50 + "px")
.css("top", relY - 50 + "px")
.css("background-position", (picWidth-relX-50) + "px "
+ (picHeight-relY-50) + "px")
});
.hover-container {
position: relative;
}
.hover-image {
position: absolute;
width: 100px;
height: 100px;
background: url(https://i.imgur.com/Hp5pUVA.jpg);
background-position: 0 0;
}
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<div class="hover-container">
<img class="hover-over" src="https://i.imgur.com/j0yhQez.jpg"/>
<div class="hover-image"></div>
</div>
Related
I'm trying to solve an issue with css "position:fixed" property on mobile browsers. I have a fixed div:
<div id="logo">
...other content here...
</div>
with css:
#logo{
position: fixed;
-webkit-backface-visibility: hidden;
bottom: 100px;
right: 0px;
width: 32px;
height: 32px;
}
So, usually the behaviour is exactly the desired one, with the div position always on the bottom right of the window, indipendently of the scroll position.
My issue is that on mobile browsers, when the users zoom the page, after a certain zoom level the div position is wrong (sometimes the div disappear out of the window).
I know that fixed position is not well supported on mobile browsers, but I wonder if there is some workaround. I tried with this js code onScroll event:
window.addEventListener('scroll', function(e){
drag.style['-webkit-transform'] = 'scale(' +window.innerWidth/document.documentElement.clientWidth + ')';\\I want to avoid zoom on this element
var r = logo.getBoundingClientRect();
var w = window.innerWidth;
var h = window.innerHeight;
if(r.right != w){
rOff = r.right - w;
logo.style.right = rOff;
}
if(r.top+132 != h){\
tOff = r.top + 132 - h;
logo.style.bottom = tOff;
}
});
Unfortunately, the code seems to return the wrong position.
Does anyone have any tip?
Ok, that's how I solved the issue...I hope that could help anyone to simulate fixed position on iOS devices.
I switched the position from fixed to absolute;
Attach to window a listener to get the new position when the page is scrolled or zoomed,
setting window.onscroll and window.onresize events with the following function:
function position() {
drag.style.left = window.innerWidth + window.pageXOffset - 32 + 'px';
drag.style.top = window.innerHeight + window.pageYOffset - 132 + 'px';
}
Do you want to catch if zoom is active?
There's no window.onZoom listener, but you can read this thread:
Catch browser's "zoom" event in JavaScript
and this answer: https://stackoverflow.com/a/995967/3616853
There's no way to actively detect if there's a zoom. I found a good entry here on how you can attempt to implement it.
I’ve found two ways of detecting the zoom level. One way to detect zoom level changes relies on the fact that percentage values are not zoomed. A percentage value is relative to the viewport width, and thus unaffected by page zoom. If you insert two elements, one with a position in percentages, and one with the same position in pixels, they’ll move apart when the page is zoomed. Find the ratio between the positions of both elements and you’ve got the zoom level. See test case. http://web.archive.org/web/20080723161031/http://novemberborn.net/javascript/page-zoom-ff3
You could also do it using the tools of the above post. The problem is you're more or less making educated guesses on whether or not the page has zoomed. This will work better in some browsers than other.
There's no way to tell if the page is zoomed if they load your page while zoomed.
Just a theory, but you may want to try setting the bottom/right positions in % rather than px.
I think what you're seeing when using pixel measurements is just the zoom effecting the pixels. Or to put it better, when you zoom-in the pixels appear larger and that throws off the position of the element, even pushing it out of the view-port on smaller screens.
Example using pixel positioning
Notice that even on a desktop as you zoom-in and out the element appears to move up and down?
Example using percent positioning
In this example the element appears to stay in the bottom right corner, because it is always positioned at 10% from the bottom of the view-port.
#logo{
position: fixed;
-webkit-backface-visibility: hidden;
bottom:10%;
right: 0;
width: 32px;
height: 32px;
}
Having two different z-index for the logo and the rest of the page could help. Allowing zooming only to the rest of the page and not to the z-index layer where logo is included. So, this might not affect the stretching on the logo.
We can
Implement a ZOOM listener
Attach it to browser
Make the zoom listener change the zoom level of the element (modify the elements position) using z-index as a factor.
Im trying to figure out best practices in regard to performance when creating multiple DIV's at an insane rate. For example, on every .mousemove event...
$('head').append("<style>.draw {width: 20px; height: 20px; position:fixed;</style>");
$(document).mousemove(function(mouseMOVE) {
//current mouse position
var mouseXcurrent = mouseMOVE.pageX;
var mouseYcurrent = mouseMOVE.pageY;
//function to create div
function mouseTRAIL(mouseX, mouseY, COLOR) {
$('body').append("<div class='draw' style='top:" + mouseY + "px; left:" + mouseX + "px; background: " + COLOR + ";'></div>");
}
// function call to create <div> at current mouse positiion
mouseTRAIL(mouseXcurrent, mouseYcurrent, '#00F');
// Remove <div>
setTimeout(function() {
$('.draw:first-child').remove();
}, 250);
});
So, this works all nice and dandy but it's mega inefficient (especially so when I try filling in the space between each mouse move position). Here's an example...
$('head').append("<style>.draw {width: 20px; height: 20px; position:fixed;</style>");
$(document).mousemove(function(mouseMOVE) {
//current mouse position
var mouseXcurrent = mouseMOVE.pageX;
var mouseYcurrent = mouseMOVE.pageY;
// function to create div
function mouseTRAIL(mouseX, mouseY, COLOR) {
$('body').append("<div class='draw' style='top:" + mouseY + "px; left:" + mouseX + "px; background: " + COLOR + ";'></div>");
}
// function call to create <div> at current mouse positiion
mouseTRAIL(mouseXcurrent, mouseYcurrent, '#00F');
// variabls to calculate position between current and last mouse position
var num = ($('.draw').length) - 3;
var mouseXold = parseInt($('.draw:eq(' + num + ')').css('left'), 10);
var mouseYold = parseInt($('.draw:eq(' + num + ')').css('top'), 10);
var mouseXfill = (mouseXcurrent + mouseXold) / 2;
var mouseYfill = (mouseYcurrent + mouseYold) / 2;
// if first and last mouse postion exist, function call to create a div between them
if ($('.draw').length > 2) {
mouseTRAIL(mouseXfill, mouseYfill, '#F80');
}
// Remove <div>
setTimeout(function() {
$('.draw:first-child').remove();
$('.draw:nth-child(2)').remove();
}, 250);
});
I really cant figure out how to improve things. Believe me, Ive tried researching but it hasn't done much good... What I'm looking for is some suggestions, examples, or links to better practices...
Please note that I'm teaching myself to code. I'm a Graphic Design student and this is how I'm spending my summer out of class... Making little projects to teach myself JavasSript, fun stuff :)
Ive set up some jsfiddles to show what Im working on...
Mouse Trail, More Elements - Very Very Slow
Mouse Trail, Less Elements - Very Slow
Mouse Trail, Bare Bones - Slow
There are multiple bad practices going on here:
Using elements instead of Canvas
Using those elements via jQuery
Abusing that jQuery as if you were trying to make it slow on purpose
Stuffing all of the above inside a mousemove handler
The root issue here really is using elements instead of canvas. After fixing that, the interaction with DOM should become minimal and thus
fix the other points as well.
Also, those who claim that this works fine didn't check their CPU usage. On my Core I5-2500K one core is instantly maxed up which is ridiculous and unacceptable for something trivial like rendering a mouse trail on screen.
I can very well imagine this being very very slow on an older computer. So yes, it's smooth but at the cost of using amount of resources enough for 10-20+ tabs to do the same properly.
This takes 7-14% cpu for me when moving mouse around fast, this takes full 25%.
You should be careful not to cause a reflow and stick only to a repaint. -> When does reflow happen in a DOM environment?
So creating <div>s is no option. - But you don't need to :)
Just create as many <div>s as you will need in future and then reposition them. If you have them in an array you'd only need an integer that points to the current most one and on each mouse movement you'd increase that value (set it to 0 once it reaches the array lenght) and reposition the <div> that's pointed to by that number.
take a look at the first panel (in red) on the homepage.
http://www.boomtown.co.za/
I'd like to do something like this with an invisible image and only reveal parts of it as the mouse tracks over. Is this possible without using Flash?
This can be done quite easily using some css and background positioning with javascript. Here's 2 examples : http://jsbin.com/ococal/3
The source code is quite easy to understand and you can start working out with this.
You could do it by using a transparent png image that was a radial fade from transparent in the centre to semi-transparent at the edges and making it follow the mouse.
document.onmousemove=mousefollower
function mousefollower(e){
x = (!document.all)? e.pageX : event.x+document.body.scrollLeft;
y = (!document.all)? e.pageY : event.y+document.body.scrollTop;
document.getElementById('myImage').style.left = x + 'px';
document.getElementById('myImage').style.top = y + 'px';
}
Obviously you can use jQuery for this too, and set the mousemove function to occur only over a specific div. Also make sure the image you use is large enough (at least twice the size) so that the edges don't show up when you move to the far sides of the div (this means that for large areas you will need a huge image so it may get a big laggy). Put the image in the div and set overflow to none to clip anything that falls outside of the area.
It is possible yes, but only in modern browsers (chrome, safari, firefox, opera).
You would need to have two <div>'s
like so..
<div class="container">
<div class="revealer"></div>
</div>
and CSS like so
.container {
position: relative;
background: url("images/your-background.jpg");
}
.revealer {
position: absolute;
//set the mask size to be the size of the container
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 1;
background: url("images/your-background-over-state.jpg");
//css3 image masks, this is not cross browser, see the demo for
// cross browser syntax
mask: url("images/mask-shape.png") no-repeat;
//make sure the mask is off screen at first, by setting the mask position
//to minus the width and height of your mask image
mask-position: -300px -300px
}
And the JS
window.addEventListener('load',function(){
var background = document.querySelector('.container'),
revealer = document.querySelector('.revealer');
background.addEventListener('mousemove', function(e){
//the minus represents the half the width/height of your mask image
// to make the reveal centred to the mouse.
var x = e.offsetX - 150,
y = e.offsetY - 150;
// move the position of the mask to match the mouse offsets
revealer.style.maskPosition = x+'px '+y+'px';
return false;
});
});
Because of the way this works you need to ensure that any other content in the .container has a higher z-index than the mask to ensure the content is not masked. To do this add relative positioning to the elements in the container
like so
.container *:not(.revealer) {
position: relative;
z-index: 2;
}
Images used in masks are images where the solid colours create the visible or fill area, and the transparent areas are the mask or cut out.
Demo with cross browser code
How can I make a picture follow the mouse in a specific <div>?
I know that I can get the mouse position from e.pageX & e.pageY and with the code document.onmousemove = followmouse;. Run the followmouse function every moment the mouse move in a page and in the followmouse function, set the picture position to the mouse position. For the exact question I asked here (how can I make a picture follow the mouse in a specific <div>), I have this idea:
Get my div top, left, width, and height and do some math and if mouse go out of the div, set visibility:hidden for the picture.
But isn't there any simple way to do this?
Let's assume you have some HTML like this,
<div id="mydiv" style="width: 300px; height: 300px;"></div>
<img id="myimg" style="position: absolute;" alt="" />
then
document.getElementById("mydiv").onmousemove = function(e) {
document.getElementById("myimg").style.top = e.pageY*1 + 5 + "px";
document.getElementById("myimg").style.left = e.pageX*1 + 5 + "px";
}
would move your picture to the mouse only if the mouse is over the div.
So long as the picture is actually contained in the div and you move it relative to its normal position, then I think setting overflow:hidden on the containing div should work. When I say "setting", I don't mean every time the mouse moves outside, but just once in the main CSS.
I'm trying to write my own lightbox script but I'm stuck on a problem.
The wrapper div centering is done through position: absolute and top / left positioned by calculating...
top:
_center_vertical = function() {
return (($(window).height() - wrapper.height()) / 2) - (options.margin + options.border) + $(window).scrollTop()
}
left:
_center_horizontal = function() {
return (($(window).width() - wrapper.width()) / 2) - (options.margin + options.border) + $(window).scrollLeft()
}
The wrapper div is centered on .load() and on $(window).resize() / $(window).scroll().
When the image is loaded and appended to wrapper, top and left is calculated using the functions above, horizontal centering is correct, but vertical centering is not. It is off by around 10px or more.
When the browser window is resized or scrolled, it calls the function which animates the centering which uses the same function to calculate the top and left. The window resize / scroll does center the image properly.
I have tried using jQuery deferred.then() to have it calculate the top / left after the image is appended, but it didn't change anything.
Example: http://jsfiddle.net/vfMNQ/
I initially thought that the difference in top position changed when I played around with things like wrapper padding (aka my border), however, I found that I was wrong.
I added some console.log('image load height: ' + ((($(window).height() - wrapper.height()) / 2) - (options.margin + options.border)) + 'px') to .load() and .scroll() and found that the difference was oddly 21px no matter what. The default border is 10px, margin is 30... so where did the 21 come from?
I'd hate to use + 21 as a hack, but seems like nobody can figure it out.
Your problem appears to be in the loading div:
.lbe-loading {
background: #578DB2 url(/public/images/loading.gif) no-repeat center center;
width: 32px;
height: 32px;
padding: 5px;
}
height:
32 + padding: (5 * 2) = 42
42 / 2 = 21px
Looks like you've appended the image with the loading div still appended to the wrapper.
wrapper.append(loading);
...
$(function() {
var img = $(new Image());
img.load(function() {
wrapper.append(this) // .lbe-loading still appended here
.css({ // Position wrapper.
...
});
loading.remove(); // Too late.
If I remove .append(loading), it centers fine.
Put .lbe-loading on a different div so it's not being added to the wrapper's height.
Best guess:
You are trying to calculate the height of the wrapper before actually putting in the image.
i.e. you are append(this) and then immediately trying to calculate the height before giving the browser a chance to display and load the image.
When I put in debugging code wrapper.height() changed by 40 pixels after resizing the display. 40 pixels is exactly the border + margin. (And when I changed those, the difference changed too.)
Its a delay in wrapper getting the height and width of the image. The jQuery for centering is executing before Browser has preformed its reflow and given 'wrapper' the height and width of the image.
I forked your fiddle and fixed it here: http://jsfiddle.net/3th5k/
by setting wrapper's height and width with javascript before centering. This way your centering calculation draws from the right data source, the javascript image object rather than the dom.
Also please note that the css statement with 'opacity: 0' has been replaced with .hide(); I did this because opacity and ie are not friends and it would most likely cause a problems down the line.
Cheers!