I have a project where I want a div to appear as a large box and three more to appear underneath as smaller boxes and when you click a smaller box, it switches sizes and places with the large box using css transitions to make the movement and size change smooth. Right now I'm attempting to use jQuery and the positioning is not working at all. Here's an example of what I have so far:
https://jsfiddle.net/v3pmhawj/1/
$(function () {
let { left: x1, top: y1 } = $('.full-size-card').offset()
$('.inactive-sheets .card').on('click', function() {
let { left: x2, top: y2 } = $(this).offset()
let curr = $('.full-size-card')
let diffX = x2 - x1
let diffY = y2 - y1
$(this).css({
left: -diffX,
top: -diffY
})
$(this).addClass('full-size-card')
curr.css({
left: diffX,
top: diffY
})
curr.removeClass('full-size-card')
})
})
If anyone has suggestions on ways that involve other libraries or other techniques, I'm all ears. I'd like to be able to move the divs around in the DOM as well but as far as I can tell, you can't css-transition them if you do that since the only way (I know of) is to delete and re-add a copy of the element where you want it in the DOM.
You can create animation effect using transitions only. To achieve this you will have to define width and height of your containers as well as top and left position of bottom elements.
On click, you just have to exchange classes of element that will become small and of element that will become large.
Here is fiddle of an example:
https://jsfiddle.net/fkd3ybwx/210/
HTML
<div class="card-container">
<div class="card large">A</div>
<div class="card small">B</div>
<div class="card small">C</div>
<div class="card small">D</div>
</div>
CSS
.card-container {
position: relative;
}
.card {
transition: all ease 1s;
position: absolute;
font-size: 24px;
border: white 4px solid;
box-sizing: border-box;
cursor: pointer;
}
.small {
width: 100px;
height: 100px;
background: blue;
left: 0;
top: 300px;
}
.small ~ .small {
left: 100px;
background: green;
}
.small ~ .small ~ .small {
left: 200px;
background: yellow;
}
.large {
width: 300px;
height: 300px;
background: red;
left: 0px;
top: 0px;
}
JavaScript
const smallCards = document.querySelectorAll('.card');
smallCards.forEach((smallCard) => {
smallCard.addEventListener('click', (event) => {
const largeCard = document.querySelector('.large');
largeCard.className = "card small";
event.target.className = "card large";
});
});
Related
I've made a tiny example below showcasing the behavior I currently get and the behavior I want.
// Rotated div
rotated.style.left = "50px";
rotated.style.top = "100px";
// Original "untouched" div
original.style.left = "50px";
original.style.top = "100px";
// Where the rotated div *should* be
expected.style.left = "-10px";
expected.style.top = "160px";
div {
position: absolute;
height: 80px;
width: 200px;
opacity: 0.5;
mix-blend-mode: overlay;
}
#rotated {
transform: rotateZ(90deg);
background: blue;
}
#original {
background: red;
}
#expected {
transform: rotateZ(90deg);
background: green;
}
<div id="rotated"></div>
<div id="original"></div>
<div id="expected"></div>
The red div is the "original" div that I have not applied any transformations to. The blue div is rotated by 90 degrees. The red and blue div are both shifted by the same values, but clearly their corners don't line up. The green div is the expected (desired) position of the blue div.
As you can see, the left and top is not really working as desired. I understand why it isn't, but I'm looking for some solutions or workarounds. I have searched online and found the transform-origin property but I've got some problems using it. This is because the elements I'm looking to move are created dynamically. They have unknown widths and heights, and on top of that, the widths and heights will change later on!
I know for this static example I can just add transform-origin: 40px 40px; to (which is just the height / 2 twice) div#rotated and it'll work, but in my project that means I'd have to set this property on every element and update it every time I update the element's dimensions.
I just don't think this is that great and I'm looking for one of two possible solutions:
A pure CSS solution that somehow gets the height of the selected element and uses that as the transform-origin (or just any pure CSS solution that works)
Using JavaScript to calculate the corrected position (in this static example, I should be able to get -10, 160 as the position of the element) every time I want to move an element.
--- update ---
This problem is further complicated because if the rotation is 180deg or 270deg then the transform-origin of 40px 40px no longer works. I'd have to compute a new transform-origin every time I want to move an element... This is something I'd really like to avoid...
You could add a translate in your expected transform
.wrapper {
position: relative;
margin: 200px 0 0 200px;
width: 200px;
height: 200px;
border: 1px solid black;
}
div {
position: absolute;
height: 80px;
width: 200px;
opacity: 0.5;
mix-blend-mode: overlay;
}
#rotated {
transform: rotateZ(90deg);
background: blue;
}
#original {
background: red;
}
#expected {
transform: rotateZ(90deg) translateY(-100%);
background: green;
transform-origin: 0 0;
}
<div class="wrapper">
<div id="rotated"></div>
<div id="original"></div>
<div id="expected"></div>
</div>
I put all in a wrapper with a border. Transform origin is from bounding box
second snippet with several transform rotated + translate
if I still didn't understand exactly the exact rotation you need, you can play with parameters translate:
translateY
translateX
translate with 2 values
always check the wrapper around it's the bounding box: 0 0 refers to top left
.wrapper1 {
position: relative;
top: 100px;
left: 100px;
width: 200px;
height: 200px;
border: 1px solid black;
}
.wrapper2 {
position: relative;
top: 250px;
left: 100px;
width: 200px;
height: 200px;
border: 1px solid black;
}
.wrapper3 {
position: relative;
top: 400px;
left: 100px;
width: 200px;
height: 200px;
border: 1px solid black;
}
.wrapper4 {
position: relative;
top: 550px;
left: 100px;
width: 200px;
height: 200px;
border: 1px solid black;
}
div {
position: absolute;
height: 80px;
width: 200px;
opacity: 0.5;
mix-blend-mode: overlay;
}
.rotated1 {
transform: rotateZ(90deg);
background: blue;
}
.rotated2 {
transform: rotateZ(90deg) translateY(-100%);
transform-origin: 0 0;
background: blue;
}
.rotated3 {
transform: rotateZ(90deg) translateY(-50%);
transform-origin: 0 0;
background: blue;
}
.rotated4 {
transform: rotateZ(90deg) translate(-50%, -50%);
transform-origin: 0 0;
background: blue;
}
.original {
background: red;
}
<div class="wrapper1">
<div class="rotated1"></div>
<div class="original"></div>
</div>
<div class="wrapper2">
<div class="rotated2"></div>
<div class="original"></div>
</div>
<div class="wrapper3">
<div class="rotated3"></div>
<div class="original"></div>
</div>
<div class="wrapper4">
<div class="rotated4"></div>
<div class="original"></div>
</div>
For now, I've solved this by computing the correct transform-origin from the angle of rotation (which is always 0, 90, 180, or 270). The code is in TypeScript:
export function computeTransformOrigin(element: HTMLElement) {
const { width, height, transform } = getComputedStyle(element);
if (transform && transform !== "none") {
const values = transform.match(/^matrix\((.+)\)$/)?.[1].split(", ");
if (values) {
element.style.translate = "";
const [a, b] = values.map(Number);
const angle = (Math.round(Math.atan2(b, a) * (180 / Math.PI)) + 360) % 360;
if (angle === 0 || angle === 90) return parseFloat(height) / 2 + "px " + parseFloat(height) / 2 + "px";
if (angle === 180) return "center";
element.style.translate = "0 " + (parseFloat(width) - parseFloat(height)) + "px";
return parseFloat(height) / 2 + "px " + parseFloat(height) / 2 + "px";
}
}
return "center";
}
For no rotation or 90 degrees, we can get the transform origin as the height of the element divided by 2 (40px 40px). With 180 degree rotation, we use center, and if it's 270, we have to do some extra magic. The transform origin is the same as 90 degrees but we also have to translate the element down by the width minus the height.
Then when I update the angle for an element, I only need to update the transform origin at the end:
set angle(v: number) {
this.#angle = v % 360;
this.element.style.transform = `rotateZ(${v}deg)`;
if (v === 180) {
this.name.style.transform = `rotateZ(${v}deg)`;
} else {
this.name.style.transform = "";
}
this.element.style.transformOrigin = computeTransformOrigin(this.element);
}
I have a lot of elements on the page. I need to draw a circumscribed rectangle that contains all DOM elements.
For that I iterate DOM elements and get rectangle:
Array.from(firstChild.children).forEach((child: Element) => {
const rect = child.getBoundingClientRect();
});
Which props I need from rect to do that?
Logically I need to get minimal x,y of left-top corner and max bottom-right corner. But how?
To apply a border you need to set 4 values: top, left, width and height.
Getting top and left can be easily done by finding the topmost and leftmost elements. To get the other values you should do some calculation:
width = rightmost - leftmost and height = bottommost - topmost
To get rightmost and bottommost you have to loop through all elements and get their rightmost/bottommost points with the same formula, but you need to rearrange them. Here you should keep the biggest values.
//select all elements that should be inside the rectangle
const myElements = document.querySelectorAll("div#container *")
//set initial values, I made sure they are always smaller/bigger than they will be
let Top=Infinity, Left=Infinity, Bottom=-Infinity, Right=-Infinity;
for(const i of myElements){
//loop through the elements
const data = i.getBoundingClientRect()
Top = Math.min(Top, data.top)
Bottom = Math.max(Bottom, data.top+data.height)
Left = Math.min(Left, data.left)
Right = Math.max(Right, data.left+data.width)
}
console.log(Top, Left, Bottom, Right) // print out the coordinates
//set the border
//I subtract 1px bacuse of the border width
const myBorder = document.querySelector("#border")
myBorder.style.top=Top-1+"px"
myBorder.style.left=Left-1+"px"
myBorder.style.width=Right-Left+"px"
myBorder.style.height=Bottom-Top+"px"
#t1{
position: absolute;
top: 20px;
left: 40px;
width: 60px;
height: 40px;
background-color: #000;
}
#t2{
position: absolute;
top: 30px;
left: 80px;
width: 40px;
height: 70px;
background-color: #111;
}
#t3{
position: absolute;
top: 50px;
left: -40px;
width: 60px;
height: 40px;
background-color: #800;
}
#t4{
position: absolute;
top: 35px;
left: 110px;
width: 30px;
height: 40px;
background-color: #444;
}
#border{
border: solid red 1px;
position: absolute;
}
<div id="container">
<div id="t1"></div>
<div id="t2">
<div id="t3"></div>
</div>
<div id="t4"></div>
</div>
<div id="border"></div>
I have a somewhat strange behaviour in Chrome and Safari. I have a scaled (transform: scale()) container with a video and other elements inside of it. At some scalings the absolute positioned elements with a high z-index disappears and does not come back again.
How can I fix this?
Note that I cannot give the video element a negative z-index and I need to use overflow: hidden;.
Example
I have made an example that scales the outermost container up and down. At a specifik scale value the element with class .on-top (and text "I should always be on top.") disappears. When scaling down again it suddenly appears.
Link to exmaple: https://jsfiddle.net/iafiawik/Lcox1ecc/
Conclusions
It seems like the size of the element matters. The larger I make it, the larger is the scale value before it disappears.
I have also tested to set transform: scale(1.4) with CSS directly on the element and the behaviour is the same.
The issue does not exist if I:
Replace the video tag with a div
Remove position: absolute; from siblings to .on-top (that is, .below)
Remove overflow: hidden; from .content
If I move .on-top so it is placed after the video tag in the document flow
(But of course none of these workarounds work for me in reality because of project specific reasons. I also cannot give the video element a negative z-index and I need to use overflow: hidden;.)
Suggested workarounds from the community (thanks!)
Give the video tag a negative z-index (can't do this because I sometimes have elements placed behind the video)
Remove overflow: hidden; (I can't remove overflow: hidden;)
Browsers
I have seen this issue in Chrome (Mac) and Safari (Mac).
Update 1
Seems like this bug report pretty much covers my problem. However, it does not provide a fix for it.
Update 2
I've answered my own question by providing my solution to this problem.
Update 3
There are a lot of answers coming in that either modify the z-index of the video or adds translateZ to the .on-top element. Demos have shown that both of those approaches do fix the issue.
However, since my HTML structure is the output from a visual HTML editor (long story ...), I do not know what elements will be there or if they should be in front, below or next to a video. Therefore I am looking for a solution that does not require changes to individual elements that are inside the scaled element.
It looks like a bug in Chrome. Notice that when you scale the image, the element inspector keeps telling you that the size of #scaled is 1024x768:
Where as in Firefox:
Now, apparently, Chrome uses the wrong size to conclude that .on-top is completely outside .content and hides it because of hidden overflow (it should not be doing this but apparently it is trying to optimize away any element that displays above a video). Examples:
Scale: 1.225
Parent width: 1254.40
Child left: 1254.40 - (100 + 90) * 1.225 = 1021.65
Result: less than 1024 (partially inside)
Scale: 1.230
Parent width: 1259.52
Child left: 1259.52 - (100 + 90) * 1.230 = 1025.82
Result: greater than 1024 (completely outside)
Unfortunately I could not find an elegant solution. Ideally you should revise your HTML markup and CSS, perhaps align the top element with left edge. As a last resort, you can move the elements more towards left using transparent border:
var goalScale = 140;
var startScale = 100;
var currentScale = 100;
var shouldScaleUp = true;
var container = document.getElementById("scaled");
var scaleInfo = document.getElementById("scale-info");
function step() {
container.style.transform = "scale(" + (currentScale / 100) + ")";
scaleInfo.innerText = "Scale: " + (currentScale / 100);
if (currentScale === goalScale) {
shouldScaleUp = false;
}
if (currentScale === startScale) {
shouldScaleUp = true;
}
if (shouldScaleUp) {
currentScale += 0.5;
} else {
currentScale -= 0.5;
}
window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step);
.scale-info {
position: fixed;
left: 0;
top: 0;
}
#scaled {
background: #cccccc;
width: 1024px;
height: 768px;
position: fixed;
left: 200px;
top: 200px;
}
.content {
height: 100%;
overflow: hidden;
position: relative;
margin-left: auto;
margin-right: auto;
background: rgba(34, 34, 56, 0.2);
}
.below {
position: absolute;
width: 300px;
height: 300px;
right: 0px;
top: 100px;
background: purple;
z-index: 1;
opacity: 0.8;
}
.below-2 {
z-index: 3;
right: 100px;
}
.below-3 {
z-index: 4;
right: 400px;
}
.on-top {
position: absolute;
width: 50px;
right: 100px;
top: 150px;
background: pink;
z-index: 5;
padding: 20px;
/* a 200px border moves the element towards left */
border-left: 200px solid transparent;
background-clip: padding-box;
}
.on-top h1 {
font-size: 20px;
}
#video {
position: absolute;
z-index: 4;
width: 1024px;
height: 768px;
background: rgba(0, 0, 0, 0.4);
}
<div id="scale-info"></div>
<div id="scaled">
<div class="content">
<h2 class="below below-1"> I have z-index 1</h2>
<div class="on-top">
<h1> I should always be on top.<br> I have z-index 5</h1>
</div>
<h2 class="below below-2"> I have z-index 3</h2> <video id="video" src="https://www.w3schools.com/html/mov_bbb.mp4"></video>
<h2 class="below below-3"> I have z-index 4</h2>
</div>
</div>
Here you go: https://jsfiddle.net/Lcox1ecc/423/
You just need to add -webkit-transform: translateZ(0); to the .on-top class.
Happy Coding!
After spending a lot of time researching this problem and trying a lot of different approaches I've come to the conclusion that no solution fixes my problem. There are solutions that fix the problem if you are able to control the z-indexes of the elements that disappear, but I am unable to do so since the structure of the HTML is not known to be (it is the output of the HTML editor). I was looking for a solution that would not require changes to individual children to the scaled parent, but I have not found any so far.
This bug report pretty much covers my problem but it does not provide a fix for it.
I can confirm that this happens because the element is outside of the scaled containers original width and height:
The element is visible at scale(1.227) (red border indicates the original size of #scaled):
... but not at scale(1.228):
My solution is therefore to add another wrapping element outside the scaled element that is not scaled, but get its width and height properties updated according to its first child scale values. This element has overflow: hidden; and prevents elements from being visible.
This is not a perfect solution as one might experience a small gap between the scaled element and the outermost wrapping element (rounding issues), but it is the best I can do given the circumstances.
var goalScale = 140;
var startScale = 100;
var currentScale = 100;
var shouldScaleUp = true;
var container = document.getElementById("scaled");
var scaledContainer = document.getElementById("resized-container");
var scaleInfo = document.getElementById("scale-info");
function step() {
var contentWidth = 1024;
var contentHeight = 768;
container.style.transform = "scale(" + (currentScale / 100) + ")";
scaledContainer.style.width = contentWidth * ((currentScale / 100)) + "px";
scaledContainer.style.height = contentHeight * ((currentScale / 100)) + "px";
scaleInfo.innerText = "Scale: " + (currentScale / 100);
if (currentScale === goalScale) {
shouldScaleUp = false;
}
if (currentScale === startScale) {
shouldScaleUp = true;
}
if (shouldScaleUp) {
currentScale += 0.5;
} else {
currentScale -= 0.5;
}
window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step);
#resized-container {
position: fixed;
width: 1024px;
height: 768px;
overflow: hidden;
border: 10px solid red;
top: 200px;
left: 200px;
}
#scaled {
background: #cccccc;
width: 1024px;
height: 768px;
position: absolute;
transform-origin: left top;
}
.content {
height: 100%;
position: relative;
margin-left: auto;
margin-right: auto;
background: rgba(34, 34, 56, 0.2);
}
.below {
position: absolute;
width: 300px;
height: 300px;
right: 0px;
top: 100px;
background: purple;
z-index: 1;
opacity: 0.8;
}
.below-2 {
z-index: 3;
right: 100px;
}
.below-3 {
z-index: 4;
right: 400px;
}
.on-top {
position: absolute;
width: 50px;
right: -30px;
top: 150px;
background: pink;
z-index: 5;
padding: 20px;
}
.on-top h1 {
font-size: 20px;
}
#video {
position: absolute;
z-index: 4;
width: 1024px;
height: 768px;
background: rgba(0, 0, 0, 0.4);
}
<div id="resized-container">
<div id="scaled">
<div id="scale-info">
</div>
<div class="content">
<h2 class="below below-1">
I have z-index 1
</h2>
<div class="on-top">
<h1>
I should always be on top.<br /> I have z-index 5
</h1>
</div>
<h2 class="below below-2">
I have z-index 3
</h2>
<video id="video" src="https://www.w3schools.com/html/mov_bbb.mp4"></video>
<h2 class="below below-3">
I have z-index 4
</h2>
</div>
</div>
</div>
One approach, if you can modify a bit your html, is wrap your problematic elements in a container that is the same size as the video and container, with the proper z-index. That way you would have clear layers of the same size and positions, into which you can position more complex elements. Like this for example:
<div id="top-container">
<div class="on-top">
<h1>
I should always be on top.<br /> I have z-index 5
</h1>
</div>
</div>
#top-container {
width: 100%;
height: 100%;
position: absolute;
z-index: 5;
}
https://jsfiddle.net/06oykj8o/4/
I made this workaround by puttingz-index:-1; on video.
https://jsfiddle.net/Lcox1ecc/312/
I really like the answer from Salman A.
The only thing that comes to mind, would be rewriting with position: relative.
But I don't know if that is an option.
I stumbled across something similar to this last week with positioning absolute elements and transforms...
I dunno if this will help you out but here is a link.
CSS transform: translate moves postion:fixed inner Div
In the end I fixed it by using a transform: translateX(none) vs translateX(0).
Super strange behavior for sure, but the link gives some more links to help make things more clear - as in its behaving per spec.
It is happening because of overflow is hidden.Here is working link
https://jsfiddle.net/Lcox1ecc/322/
.content {
overflow:visible;
}
It might be late but just posting in case somebody finds it helpful.
Add an empty div under the parent container element with transform animation and nothing will disappear anymore. The animation does not do anything but it forces the browser to render all the elements using hardware acceleration.
<div class="emptydiv"></div>
.emptydiv{
transform:scale(1);
animation:fix 3s infinite;
}
#keyframes fix{
50%{
transform:scale(1);
}
}
I'm trying to figure out how Medium made their bottom action / menu bar slide up when your mouse enters the bottom of the document. The slide up effect is not triggered by moving the mouse over the invisible div (it slides up & down via transform translateY).
Besides, the menu bar is only 44px in height, but its is-visible class gets triggered way before your mouse is near it — but by what? When using Inspect Element, I can't see any hidden divs that could be triggering it..
I've searched for countless of ways, e.g. "show element when mouse enters specific part of document" but all search results involve when the mouse enters or moves over a div element, which is not the solution I'm looking for.
Obviously, you can solve this problem by putting the slide up menu inside a hidden container like I've done here, and then you get the desired result:
(function() {
var actionBar = document.querySelector('.action-bar');
var actionBarWrapper = document.querySelector('.action-bar-detection');
function showDiv() {
actionBar.classList.add('js-is-visible')
}
function hideDiv() {
actionBar.classList.remove('js-is-visible')
}
actionBarWrapper.onmouseover = showDiv;
actionBarWrapper.onmouseout = hideDiv;
})();
* {
margin: 0;
padding: 0;
box-sizing: border-box;
line-height: 1.5;
}
body {
height: 100%;
}
.wrapper {
width: 90%;
max-width: 600px;
margin: 5% auto;
}
.action-bar {
position: absolute;
left: 0;
bottom: 0;
border: 1px solid #252321;
background: #fff;
padding: 16px;
width: 100%;
min-height: 50px;
opacity: 0;
transform: translateY(100%);
transition: all .5s;
z-index: 99;
}
.action-bar-detection {
height: 150px;
width: 100%;
opacity: 1;
position: fixed;
bottom: 0;
left: 0;
z-index: 1;
}
.js-is-visible {
opacity: 1;
transform: translateY(0%);
}
<html>
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<div class="wrapper">
<p>When mouse enters the hidden action bar element, slides up.</p>
<p>But it's only happening because the action-bar is inside an invisible detection layer class (action-bar-detection) with a height of 150px.</p>
</div>
<div class="action-bar-detection">
<div class="action-bar">
Bottom Menu
</div>
</div>
</body>
</html>
However, this doesn't seem to be what Medium have done, and if this can be done without adding more HTML & CSS, I want to learn how! :-)
I think I'm not phrasing the problem correctly, since I can't find any solutions even remotely close (I've searched A LOT).
Any advice? What should I read up on? :-)
Get height of viewport, track onmousemove, and compare clientY from the mouse event to the viewport height:
(function() {
var actionBar = document.querySelector('.action-bar');
var viewHeight = window.innerHeight - 150;
function toggleDiv(e) {
if (e.clientY >= viewHeight) {
actionBar.classList.add('js-is-visible');
} else {
actionBar.classList.remove('js-is-visible');
}
}
window.onmousemove = toggleDiv;
})();
* {
margin: 0;
padding: 0;
box-sizing: border-box;
line-height: 1.5;
}
body {
height: 100%;
}
.wrapper {
width: 90%;
max-width: 600px;
margin: 5% auto;
}
.action-bar {
position: absolute;
left: 0;
bottom: 0;
border: 1px solid #252321;
background: #fff;
padding: 16px;
width: 100%;
min-height: 50px;
opacity: 0;
transform: translateY(100%);
transition: all .5s;
z-index: 99;
}
.action-bar-detection {
height: 150px;
width: 100%;
opacity: 1;
position: fixed;
bottom: 0;
left: 0;
z-index: 1;
}
.js-is-visible {
opacity: 1;
transform: translateY(0%);
}
<div class="wrapper">
<p>When mouse comes within 150px of the bottom part of the screen, the bar slides up.</p>
<p>When the mouse leaves this defined area of the screen, the bar slides down.</p>
</div>
<div class="action-bar-detection">
<div class="action-bar">
Bottom Menu
</div>
</div>
You could do this by listening to the mousemove event on the document, you will want to invest effort into making this performant as it will be triggered frequently. The most common way to regulate events like this is through throttling.
Once you are hooked into the mousemove event you will need to get the Y coordinate of the cursor and compare that to the height of the window, if it is within a threshold then you can reveal your panel, once it moves out you can proceed to hide it again.
Here is an example showing a basic implementation jsFiddle
// Using underscore for the throttle function though you can implement your own if you wish
document.addEventListener('mousemove', _.throttle(mouseMoveEventAction, 200));
function mouseMoveEventAction(e) {
doPanelStuff(isInsideThreshold(e.clientY));
}
function doPanelStuff(isActive) {
var panelElement = document.querySelector('.panel');
if (isActive) {
panelElement.style.background = 'red';
} else {
panelElement.style.removeProperty('background');
}
}
function isInsideThreshold(cursorY) {
var threshold = 200;
var clientHeight = document.documentElement.clientHeight;
return cursorY > (clientHeight - threshold);
}
html, body {
height: 100%;
}
.container, .content {
height: 100%;
width: 100%;
}
.panel {
height: 50px;
position: absolute;
bottom: 0;
width: 100%;
background: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<div class="container">
<div class="content"></div>
<div class="panel"></div>
</div>
I want to create a jQuery slime menu. The basic concept is simple. I have some icons with fixed positions on the screen. Some of them are "switches", and if you click them, you need to click on another element to have some effect (imageine something like when you have a building in an RTS. It's not enough to just click on the building's icon, but you need to place it on the map after that action).
I want to display a CSS triangle element after you clicked the icon, and before you click on another "compatible" div. The problem is not here, but I'm not so familiar with CSS transformations.
Here's a fiddle about what I have so far. My problem is that the arrow element is not rotating to the cursor. I also need to change the size of it, so the triangle's bottom center must be at the pointer.
http://jsfiddle.net/PSYKLON/41Lcj653/
The HTML part is simple (the span element is used for debugging):
<div class="fixicon"></div>
<div class="arrow"></div>
<span></span>
The CSS:
span {
float: right;
}
.fixicon {
width: 30px;
height: 30px;
background: #111;
border-radius: 15px;
position: fixed;
left: 10px;
top: 10px;
}
.arrow {
width: 0;
height: 0;
border-style: solid;
border-width: 0 25px 200px 25px;
border-color: transparent transparent #111 transparent;
opacity: 0;
position: fixed;
left: 0;
top: 25px;
transform-origin: 25px 0px;
transform: rotate(0deg);
}
.arrow.show {
opacity: 1;
}
And the JS:
$(function(){
$('.fixicon').click(function(){
$('.arrow').toggleClass('show');
});
$(window).mousemove(function(e){
if(!$('.arrow').hasClass('show')) {
return;
}
var dir = point_direction(25,25,e.pageX,e.pageY);
$('.arrow').css('transform','rotate('+dir+'deg)');
$('span').text(dir);
});
});
function point_direction(x1,y1,x2,y2) {
var dx,dy;
dy = y2 - y1;
dx = x2 - x1;
return (Math.atan(dy/dx) * 360) % 360;
}
So basically, I need to achieve something like this, doesn't matter where the cursor on the screen are:
Thanks for anyone, who can help. :)