I'm trying to create some very basic tooltips but I'm having trouble calculating the exact position these should go in with some JavaScript. The reason for wanting a fixed position is to make sure these work whenever there is overflow hidden and such.
This is my code so far:
var overflowTooltip = function (elem) {
let legendRow = elem.currentTarget.getBoundingClientRect();
let tooltip = elem.currentTarget.children[2];
let topPosition;
let leftPosition;
if ((elem.currentTarget.offsetWidth < elem.currentTarget.scrollWidth) && tooltip !== undefined) {
tooltip.classList.add('total-opacity');
$timeout(function () {
if (tooltip.offsetHeight > 35) {
topPosition = (legendRow.top - tooltip.offsetHeight / 4) - 65;
} else {
topPosition = legendRow.top - 65;
}
leftPosition = (legendRow.left + elem.currentTarget.offsetWidth) / 2;
tooltip.style.left = leftPosition + 'px';
tooltip.style.top = topPosition + 'px';
$timeout(function () {
tooltip.classList.remove('total-opacity');
}, 400)
}, 100);
} else {
elem.currentTarget.children[2].style.left = '-9999px';
}
}
And some SASS:
.custom-tooltip {
font-family: $brand-font-condensed;
font-size: 14px;
font-weight: 400;
position: fixed;
text-align: left;
overflow: visible !important;
background-color: rgba($dark-gray, 0.95);
color: #fff;
height: auto;
padding: 7px 10px;
z-index: 9000;
visibility: hidden;
opacity: 0;
border-radius: 2px;
box-shadow: 0 2px 5px 0 rgba(#000, 0.16), 0 2px 10px 0 rgba(#000, 0.12);
#include transition (.2s ease-in);
left: -9999px;
&:hover {
visibility: visible;
opacity: 1;
#include transition (.2s ease-out);
-webkit-transition-delay: .3s;
transition-delay: .3s;
}
}
The above works but it's not perfect. if I wanna change around the position for it to appear on the left, right, bottom. I'd have to so some refactoring. If the tooltip's height is bigger or smaller, the position changes, If I scroll up or down, the tooltip stays stuck on screen for a few seconds. etc. Lots of these little details which are pretty annoying.
Not interested in using a plugin at the moment nor jQuery. Thanks for any suggestion or feedback :)
Related
I have a small page. Divas in the form of circles are created here every certain time.
They spawn in random places.
As can be seen even on the buttons and slightly outside the page.
The question is. Is it possible to make a box that does not touch the buttons, and that the circles are created within this box?
This should be done as a border with a certain extension, but specifying everything in pixels is not an option, it will be bad for different screens.
I created such a frame, replaced document.body.appendChild(div);
on the document.getElementById("spawnRadius").appendChild(div);
It seems that they should appear within this frame, but no, all the same throughout the page.
I also tried instead of whole page height and width document.documentElement.clientWidth use the width and height of the desired border spawnRadius.width
But now all my circles do not appear randomly, but at the beginning of this block in one place.
I tried to see these values through console.log
console.log(documentHeight);
console.log(documentWidth);
But getting there undefined
PS. Demo watch in full page
//timer
var minutesLabel = document.getElementById("minutes");
var secondsLabel = document.getElementById("seconds");
var totalSeconds = 0;
setInterval(setTime, 1000);
function setTime() {
++totalSeconds;
secondsLabel.innerHTML = pad(totalSeconds % 60);
minutesLabel.innerHTML = pad(parseInt(totalSeconds / 60));
}
function pad(val) {
var valString = val + "";
if (valString.length < 2) {
return "0" + valString;
} else {
return valString;
}
}
//create circle
var widthHeight = 65;
var margin = 25;
var delta = widthHeight + margin;
var spawnRadius = document.getElementById("spawnRadius");
let clicks = 0;
function createDiv(id, color) {
let div = document.createElement('div');
var currentTop = 0;
var documentHeight = spawnRadius.height;
var documentWidth = spawnRadius.width;
div.setAttribute('class', id);
if (color === undefined) {
let colors = ['#35def2', '#35f242', '#b2f235', '#f2ad35', '#f24735', '#3554f2', '#8535f2', '#eb35f2', '#f2359b', '#f23547'];
div.style.borderColor = colors[Math.floor(Math.random() * colors.length)];
}
else {
div.style.borderColor = color;
}
div.classList.add("circle");
div.classList.add("animation");
currentTop = Math.floor(Math.random() * documentHeight) - delta;
currentLeft = Math.floor(Math.random() * documentWidth) - delta;
var limitedTop = Math.max(margin * -1, currentTop);
var limitedLeft = Math.max(margin * -1, currentLeft);
div.style.top = limitedTop + "px";
div.style.left = limitedLeft + "px";
const nodes = document.querySelectorAll('.animation');
for(let i = 0; i < nodes.length; i++) {
nodes[i].addEventListener('click', (event) => {
event.target.style.animation = 'Animation 200ms linear';
setTimeout(() => {
event.target.style.animation = '';
}, 220); });
}
$(div).click(function() {
$('#clicks').text(++clicks);
$(this).fadeOut();
});
document.getElementById("spawnRadius").appendChild(div);
}
let i = 0;
const oneSecond = 600;
setInterval(() => {
i += 1;
createDiv(`circle${i}`);
}, oneSecond);
html, body {
width: 100%;
height: 100%;
margin: 0;
background: #0f0f0f;
}
.back {
font-family: "Comic Sans MS", cursive, sans-serif;
font-size: 25px;
letter-spacing: 2px;
word-spacing: 2px;
color: #ffffff;
text-shadow: 0 0 5px #ffffff, 0 0 10px #ffffff, 0 0 20px #ffffff, 0 0 40px #ff00de, 0 0 80px #ff00de, 0 0 90px #ff00de, 0 0 100px #ff00de, 0 0 150px #ff00de;
font-weight: 700;
text-decoration: none;
font-style: italic;
font-variant: normal;
text-transform: lowercase;
position: absolute;
top: 25%;
left: 2%;
user-select: none;
z-index: 999;
}
.panel {
color: #0f0f0f;
font-size: 40px;
z-index: 999;
position: absolute;
cursor: default;
user-select: none;
color: #0f0f0f;
}
.score {
border: 1px solid #ffffff;
padding: 5px;
background-color: #ffffff;
border-radius: 40px 10px;
}
.time {
border: 1px solid #ffffff;
padding: 5px;
background-color: #ffffff;
border-radius: 40px 10px;
}
.circle {
width: 60px;
height: 60px;
border-radius: 60px;
background-color: #0f0f0f;
border: 3px solid #000;
margin: 20px;
position: absolute;
}
#keyframes Animation {
0% {
transform: scale(1);
}
50% {
transform: scale(.8);
}
100% {
transform: scale(1);
}
}
#spawnRadius {
top: 55%;
height: 650px;
width: 1000px;
left: 50%;
white-space: nowrap;
position: absolute;
transform: translate(-50%, -50%);
background: #0f0f0f;
border: 2px solid #ebc6df;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<span class="panel">
<span class="score">Score: <a id="clicks">0</a></span>
<span class="time">Time: <label id="minutes">00</label>:<label id="seconds">00</label></span>
</span>
back
<div id="spawnRadius"></div>
To answer your main question, the getBoundingClientRect method can be used to retrieve the current bounding rectangle of an element, using which you can determine where the valid spawn areas are.
When choosing a valid placement, only consider the width and height of the container element, since the coordinates of child elements are relative to its parent. You also need to take into account the size of the elements being spawned, so the valid range of the x position for example is 0 to containerWidth - circleWidth.
The circles also had a CSS margin associated with them, which would offset them past their absolute coordinates.
There are a few other issues with the code though which you may run into later on:
There was an odd mix of jQuery and standard JavaScript calls, so if you're familiar with native JavaScript methods then it's likely simpler to stick with those and remove the dependency on jQuery.
For example, there were two click event handlers on each circle, one to add the CSS animation and another to increment the score. These can be combined into a single function.
The bounce out animation and the jQuery fade out can also be combined by adding opacity values into the animation start and end keyframes.
There was a loop in the createDiv function which added another click event handler to every circle element rather than just to the newly created element. This may have originally necessitated the jQuery click handler outside of that loop, since otherwise the score counter would have been incremented multiple times.
It was also possible to click the circles multiple times before the animation was complete (hence adding multiple points), which was likely not intended. Adding a simple Boolean clicked flag can avoid this.
Once the fade animation completed, the circle element itself was still on the page, it just had a display of none so wouldn't be visible. Over time, this would cause slowdowns on lower end hardware since there would be many DOM elements still sitting in memory that were no longer required. As such, it's best to remove elements from the DOM once they're no longer needed using removeChild. You had the right idea by removing the animation after the animation completed.
Here's the amended code:
var minutesLabel = document.getElementById("minutes");
var secondsLabel = document.getElementById("seconds");
var clickEl = document.getElementById("clicks");
var totalSeconds = 0;
let clicks = 0;
setInterval(setTime, 1000);
function setTime() {
++totalSeconds;
secondsLabel.innerText = pad(totalSeconds % 60);
minutesLabel.innerText = pad(parseInt(totalSeconds / 60));
}
function pad(val) {
var valString = val + "";
if (valString.length < 2) {
return "0" + valString;
} else {
return valString;
}
}
var spawnRadius = document.getElementById("spawnRadius");
var spawnArea = spawnRadius.getBoundingClientRect();
const circleSize = 66; // Including borders
//create circle
function createDiv(id, color) {
let div = document.createElement('div');
div.setAttribute('class', id);
if (color === undefined) {
let colors = ['#35def2', '#35f242', '#b2f235', '#f2ad35', '#f24735', '#3554f2', '#8535f2', '#eb35f2', '#f2359b', '#f23547'];
div.style.borderColor = colors[Math.floor(Math.random() * colors.length)];
}
else {
div.style.borderColor = color;
}
// Randomly position circle within spawn area
div.style.top = `${Math.floor(Math.random() * (spawnArea.height - circleSize))}px`;
div.style.left = `${Math.floor(Math.random() * (spawnArea.width - circleSize))}px`;
div.classList.add("circle", "animation");
// Add click handler
let clicked = false;
div.addEventListener('click', (event) => {
if (clicked) { return; } // Only allow one click per circle
clicked = true;
div.style.animation = 'Animation 200ms linear forwards';
setTimeout(() => { spawnRadius.removeChild(div); }, 220);
clickEl.innerText = ++clicks
});
spawnRadius.appendChild(div);
}
let i = 0;
const oneSecond = 1000;
setInterval(() => {
i += 1;
createDiv(`circle${i}`);
}, oneSecond);
html, body {
width: 100%;
height: 100%;
margin: 0;
background: #0f0f0f;
}
.back {
font-family: "Comic Sans MS", cursive, sans-serif;
font-size: 25px;
letter-spacing: 2px;
word-spacing: 2px;
color: #ffffff;
text-shadow: 0 0 5px #ffffff, 0 0 10px #ffffff, 0 0 20px #ffffff, 0 0 40px #ff00de, 0 0 80px #ff00de, 0 0 90px #ff00de, 0 0 100px #ff00de, 0 0 150px #ff00de;
font-weight: 700;
text-decoration: none;
font-style: italic;
font-variant: normal;
text-transform: lowercase;
position: absolute;
top: 25%;
left: 2%;
user-select: none;
z-index: 999;
}
.panel {
color: #0f0f0f;
font-size: 40px;
z-index: 999;
position: absolute;
cursor: default;
user-select: none;
color: #0f0f0f;
}
.score {
border: 1px solid #ffffff;
padding: 5px;
background-color: #ffffff;
border-radius: 40px 10px;
}
.time {
border: 1px solid #ffffff;
padding: 5px;
background-color: #ffffff;
border-radius: 40px 10px;
}
.circle {
width: 60px;
height: 60px;
border-radius: 60px;
background-color: #0f0f0f;
border: 3px solid #000;
position: absolute;
}
#keyframes Animation {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(.8);
}
100% {
transform: scale(1);
opacity: 0;
}
}
#spawnRadius {
top: 55%;
height: 650px;
width: 1000px;
left: 50%;
white-space: nowrap;
position: absolute;
transform: translate(-50%, -50%);
background: #0f0f0f;
border: 2px solid #ebc6df;
}
<span class="panel">
<span class="score">Score: <a id="clicks">0</a></span>
<span class="time">Time: <label id="minutes">00</label>:<label id="seconds">00</label></span>
</span>
back
<div id="spawnRadius"></div>
I'm trying to create an image zoom effect similar to this one. I've managed to search a plugin called prefixfree.js and tried it in my code, but it did not work, its just showing the image but when I hover it there is no image zoom effect.
The link for the plugin is this. It should suppose to work like this.
Also for additional info, the size for the large image is 1406X1275 and the small image is 200X200. Kindly help me on solving this one or provide better alternatives.
$(document).ready(function() {
var native_width$ = 0;
var native_height = 0;
$(".magnify").mousemove(function(e) {
//When the user hovers on the image, the script will first calculate
//the native dimensions if they don't exist. Only after the native dimensions
//are available, the script will show the zoomed version.
if (!native_width && !native_height) {
//This will create a new image object with the same image as that in .small
//We cannot directly get the dimensions from .small because of the
//width specified to 200px in the html. To get the actual dimensions we have
//created this image object.
var image_object = new Image();
image_object.src = $(".small").attr("src");
//This code is wrapped in the .load function which is important.
//width and height of the object would return 0 if accessed before
//the image gets loaded.
native_width = image_object.width;
native_height = image_object.height;
} else {
//x/y coordinates of the mouse
//This is the position of .magnify with respect to the document.
var magnify_offset = $(this).offset();
//We will deduct the positions of .magnify from the mouse positions with
//respect to the document to get the mouse positions with respect to the
//container(.magnify)
var mx = e.pageX - magnify_offset.left;
var my = e.pageY - magnify_offset.top;
//Finally the code to fade out the glass if the mouse is outside the container
if (mx < $(this).width() && my < $(this).height( && mx > 0 && my > 0) {
$(".large").fadeIn(100);
} else {
$(".large").fadeOut(100);
}
if ($(".large").is(":visible")) {
//The background position of .large will be changed according to the position
//of the mouse over the .small image. So we will get the ratio of the pixel
//under the mouse pointer with respect to the image and use that to position the
//large image inside the magnifying glass
var rx = Math.round(mx / $(".small").width() * native_width - $(".large").width() / 2) * -1;
var ry = Math.round(my / $(".small").height() * native_height - $(".large").height() / 2) * -1;
var bgp = rx + "px " + ry + "px";
//Time to move the magnifying glass with the mouse
var px = mx - $(".large").width() / 2;
var py = my - $(".large").height() / 2;
//Now the glass moves with the mouse
//The logic is to deduct half of the glass's width and height from the
//mouse coordinates to place it with its center at the mouse coordinates
//If you hover on the image now, you should see the magnifying glass in action
$(".large").css({
left: px,
top: py,
backgroundPosition: bgp
});
}
}
})
})
* {
margin: 0;
padding: 0;
}
.magnify {
width: 200px;
margin: 50px auto;
position: relative;
}
.large {
width: 175px;
height: 175px;
position: absolute;
border-radius: 100%;
/*Multiple box shadows to achieve the glass effect*/
box-shadow: 0 0 0 7px rgba(255, 255, 255, 0.85), 0 0 7px 7px rgba(0, 0, 0, 0.25), inset 0 0 40px 2px rgba(0, 0, 0, 0.25);
/*Lets load up the large image first*/
background: url('microsoftLogo1.jpg') no-repeat;
/*hide the glass by default*/
display: none;
}
#subPic1 {
border: 1px solid black;
margin: 5px;
width: 50px;
height: 50px;
}
#subPic2 {
border: 1px solid black;
margin: 5px;
width: 50px;
height: 50px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="magnify">
<div class="large"></div>
<img class="small" src="microsoftLogo1Small.jpg" />
</div>
<script src="http://thecodeplayer.com/uploads/js/prefixfree.js" type="text/javascript"></script>
<img id="subPic1" src="microsoftLogo1.jpg" onclick="getImage1()" /><br/>
<img id="subPic2" src="microsoftLogo2.jpg" onclick="getImage2()" />
HTML
<img src="sample.png" class="zoom" />
CSS
img.zoom {
width: 350px;
height: 200px;
-webkit-transition: all .2s ease-in-out;
-moz-transition: all .2s ease-in-out;
-o-transition: all .2s ease-in-out;
-ms-transition: all .2s ease-in-out;
}
.transition {
-webkit-transform: scale(1.8);
-moz-transform: scale(1.8);
-o-transform: scale(1.8);
transform: scale(1.8);
}
JavaScript
$(document).ready(function(){
$('.zoom').hover(function() {
$(this).addClass('transition');
}, function() {
$(this).removeClass('transition');
});
});
HTML
<div class="item">
<img src="pepsi.jpg" alt="pepsi" width="540" height="548">
<div class="item-overlay top"></div>
</div>
CSS
* {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
margin: 0;
padding: 0;
}
.item {
position: relative;
border: 1px solid #333;
margin: 2%;
overflow: hidden;
width: 540px;
}
.item img {
max-width: 100%;
-moz-transition: all 0.3s;
-webkit-transition: all 0.3s;
transition: all 0.3s;
}
.item:hover img {
-moz-transform: scale(1.1);
-webkit-transform: scale(1.1);
transform: scale(1.1);
}
The question is almost the same as this: how to rotate the shadow effect with CSS?
But my question is a bit more complicated: i use "filter: drop-shadow" because object that i want to have shadow effect is composite - it consists of two primitive figures.
I achieved the desired effect with JS - just rotating the main object and then calculating drop-shadow direction. But the shadow blinks on rerendering, it is visible at least in Chrome.
(function() {
const RAD_TO_DEG = 180/Math.PI,
DEG_TO_RAD = Math.PI/180;
var arrow = document.getElementsByClassName('arrow')[0],
arrow_shadow_color = 'rgba(50,50,50,0.25)',
previous_x = 0,
previous_y = 0,
shadow_angle = -45,
shadow_blur_radius = 5,
shadow_offset = 15,
shadow_string_right = 'px ' + shadow_blur_radius + 'px ' + arrow_shadow_color + ')',
amount_of_attempts_to_skip = 10,
n = 0;
dropShadow(180);
document.addEventListener('mousemove', mouseMove);
function mouseMove(e) {
n++;
if (n%amount_of_attempts_to_skip === 0) {
var angle = Math.atan2( previous_y - e.pageY, e.pageX - previous_x ) * RAD_TO_DEG;
arrow.style.transform = 'rotate(' + (180 - ~~angle) + 'deg)';
dropShadow(angle);
previous_x = e.pageX;
previous_y = e.pageY;
}
}
function dropShadow(angle) {
angle = 180 - shadow_angle + angle;
var x = ( shadow_offset * Math.cos( angle * DEG_TO_RAD) ).toFixed(2),
y = ( shadow_offset * Math.sin( angle * DEG_TO_RAD) ).toFixed(2);
arrow.style.filter = 'drop-shadow(' + x + 'px ' + y + shadow_string_right;
}
})();
html, body {
width: 100%;
height: 100%;
box-sizing: border-box;
}
* {
margin: 0;
border: 0;
padding: 0;
position: relative;
box-sizing: inherit;
}
.container {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-100%, -50%);
}
.arrow {
width: 75px;
height: 20px;
background: #2ECC40;
transform-origin: right;
transition: all 0.15s ease;
}
.arrow:before {
content: "";
position: absolute;
border-bottom: 15px solid transparent;
border-right: 20px solid #2ECC40;
border-top: 15px solid transparent;
height: 0px;
width: 0px;
margin-left: -20px;
margin-top: -5px;
}
<div class="container"><div class="arrow"></div></div>
So the question is: is it possible to create a shadow effect for a composite object with CSS and then rotate it so that it keeps the absolute angle with CSS?
Or maybe at least with JS but some other way but manually setting x and y filter offsets.
UPD: i just realized that there is just no need to dynamically apply drop-shadow style - it can be applied to a container: there will be no rerendering flashes, no need to apply some techniques to smoothen the shadow movement, no need to manually calculate shadow offset, that's it. I answered my own question 'cuz it was silly.
I just realized that there is just no need to dynamically apply drop-shadow style - it can be applied to a container: there will be no rerendering flashes, no need to apply some techniques to smoothen the shadow movement, no need to manually calculate shadow offset, that's it. All of these will be rendered automatically.
So the answer for "is it possible to create a shadow effect for a composite object with CSS and then rotate it so that it keeps the absolute angle with CSS?" is Yes, it is possible: just apply drop-shadow filter to the container of the element that you want to have a shadow effect.
Stackoverflow, sorry for asking silly questions.
Shadow blinking is out of bug. I fixed your thing at my CodePen and below. Your project's arrow will get dynamic shadow with only CSS if you create pseudo element which will move with cursor.
That flickering of the shadow of 3D objects upon cursor move is browser specific long known CSS related kind of bug with fixes available everywhere. You only needed to know that matter. You can search StackOverflow and perform web search now. Two ways has minor difference in CSS. But both actually works. I have not changed your javascript.
You can read/see W3C docs, CSS tricks's this, CSS trick's this,W3 School and this code pen for CSS pseudo element drag-able drop shadow.
For your case I modified this :
.arrow {
width: 75px;
height: 20px;
background: #2ECC40;
transform-origin: right;
transition: all 0.01s ease;
transform-style: preserve-3d;
-webkit-transform: rotateY(60deg);
-webkit-transform-style: preserve-3d;
transform: rotateY(60deg);
(function() {
const RAD_TO_DEG = 180/Math.PI,
DEG_TO_RAD = Math.PI/180;
var arrow = document.getElementsByClassName('arrow')[0],
arrow_shadow_color = 'rgba(50,50,50,0.25)',
previous_x = 0,
previous_y = 0,
shadow_angle = -45,
shadow_blur_radius = 5,
shadow_offset = 15,
shadow_string_right = 'px ' + shadow_blur_radius + 'px ' + arrow_shadow_color + ')',
amount_of_attempts_to_skip = 10,
n = 0;
dropShadow(180);
document.addEventListener('mousemove', mouseMove);
function mouseMove(e) {
n++;
if (n%amount_of_attempts_to_skip === 0) {
var angle = Math.atan2( previous_y - e.pageY, e.pageX - previous_x ) * RAD_TO_DEG;
arrow.style.transform = 'rotate(' + (180 - ~~angle) + 'deg)';
dropShadow(angle);
previous_x = e.pageX;
previous_y = e.pageY;
}
}
function dropShadow(angle) {
angle = 180 - shadow_angle + angle;
var x = ( shadow_offset * Math.cos( angle * DEG_TO_RAD) ).toFixed(2),
y = ( shadow_offset * Math.sin( angle * DEG_TO_RAD) ).toFixed(2);
arrow.style.filter = 'drop-shadow(' + x + 'px ' + y + shadow_string_right;
}
})();
html, body {
width: 100%;
height: 100%;
box-sizing: border-box;
transform-style: preserve-3d;
-webkit-transform-style: preserve-3d;
}
* {
margin: 0;
border: 0;
padding: 0;
position: relative;
box-sizing: inherit;
transform-style: preserve-3d;
}
.container {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-100%, -50%);
transform-style: preserve-3d;
-webkit-transform-style: preserve-3d;
}
.arrow {
width: 75px;
height: 20px;
background: #2ECC40;
transform-origin: right;
transition: all 0.01s ease;
transform-style: preserve-3d;
-webkit-transform: rotateY(60deg);
-webkit-transform-style: preserve-3d;
transform: rotateY(60deg);
}
.arrow:before {
content: "";
position: absolute;
border-bottom: 15px solid transparent;
border-right: 20px solid #2ECC40;
border-top: 15px solid transparent;
height: 0px;
width: 0px;
margin-left: -20px;
margin-top: -5px;
}
<div class="container"><div class="arrow"></div></div>
Depends on what kind of solution you are looking for. If you need a lot of elements with shadows, it's better to use a prerendered image. Browser won't spend time calculating all the shadows and rotations for each element.
If you absolutely need a shadow on a composite object with CSS, use box-shadow. There is a hacky way to make a triangle with the shadow. It's much better and efficient to use an image though!
Here by rotating the wrapper element we rotate all of its children and automatically their box-shadow:
(matrix value is taken from the computed style)
<!doctype html>
<html>
<head>
<style>
#keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.arrow {
top: 150px;
left: 50px;
position: relative;
display: block;
width: 280px;
animation: rotate 5s infinite linear;
}
.arrow div {
display: inline-block;
position: absolute;
}
.arrow-body {
width: 251px;
height: 25px;
top: 16px;
background: green;
box-shadow: 1px 5px 0 0 black;
}
.arrow-head {
width: 0;
height: 0;
right: 0;
bottom: -84px;
box-sizing: border-box;
border: 1em solid black;
border-color: transparent transparent green green;
transform-origin: 0 0;
transform: rotate(225deg);
box-shadow: -5px 1px 0 0 black;
}
#log {
font-family: monospace;
}
</style>
<script>
setInterval(function(){
var a = document.getElementById("arrow");
var l = document.getElementById("log");
l.innerHTML = ".arrow { transform: " + window.getComputedStyle(a, null).getPropertyValue("transform") + " }";
}, 10);
</script>
</head>
<body>
<span id="log"></span>
<div class="arrow" id="arrow">
<div class="arrow-body"></div>
<div class="arrow-head"></div>
</div>
</body>
</html>
In the following codepen, the creator has made a material ripple effect. However there is an issue where if I add another div right next to the original the ripple will leak into it.
What should I do to change to code so that the ripple will only be contained in the div that it was activated on?
I have tried editing the JS so that the click function only activates for divs with the class ".rippleDiv" but that did not work either.
Link to codepen http://codepen.io/Ruddy/pen/09052b957d82a17bd6ca70ac6663dd6a
HTML
<div class="rippleDiv">Button</div>
<div>Button 2</div>
CSS
div {
width: 220px;
height: 120px;
background: #222;
color: #fff;
text-align: center;
line-height: 120px;
font-size: 40px;
}
/* Ripple */
.ripple {
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.4);
transform: scale(0);
position: absolute;
opacity: 1;
}
.rippleEffect {
animation: rippleDrop .6s linear;
}
#keyframes rippleDrop {
100% {
transform: scale(2);
opacity: 0;
}
}
JS
$(".rippleDiv").click(function (e) {
// Remove any old one
$(".ripple").remove();
// Setup
var posX = $(this).offset().left,
posY = $(this).offset().top,
buttonWidth = $(this).width(),
buttonHeight = $(this).height();
// Add the element
$(this).prepend("<span class='ripple'></span>");
// Make it round!
if(buttonWidth >= buttonHeight) {
buttonHeight = buttonWidth;
} else {
buttonWidth = buttonHeight;
}
// Get the center of the element
var x = e.pageX - posX - buttonWidth / 2;
var y = e.pageY - posY - buttonHeight / 2;
// Add the ripples CSS and start the animation
$(".ripple").css({
width: buttonWidth,
height: buttonHeight,
top: y + 'px',
left: x + 'px'
}).addClass("rippleEffect");
});
The basic answer is that the 'ripple' element needs to be contained inside a div that has overflow:hidden set.
However to get this right, a number of small changes need to be made so that both the original button content, as well as the ripple itself, are correctly positioned, mainly using divs with the correct positioning attributes set.
So - here are the changes I made to get this to work: http://codepen.io/kitr/pen/xgLQpM
HTML:
<div>Button</div>
<div>Button 2</div>
CSS:
div {
width: 220px;
height: 120px;
background: #222;
color: #fff;
text-align: center;
line-height: 120px;
font-size: 40px;
position:relative;
overflow:hidden;
}
/* Ripple */
.ripple {
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.4);
transform: scale(0);
opacity: 1;
}
.rippleEffect {
animation: rippleDrop .6s linear;
position: absolute;
}
#keyframes rippleDrop {
100% {
transform: scale(2);
opacity: 0;
}
}
Javascript:
$("div").click(function (e) {
// Remove any old one
$(".ripple").remove();
// Setup
var posX = $(this).offset().left,
posY = $(this).offset().top,
buttonWidth = $(this).width(),
buttonHeight = $(this).height();
// Add the element
$(this).append("<div class='ripple'></div>");
// Make it round!
if(buttonWidth >= buttonHeight) {
buttonHeight = buttonWidth;
} else {
buttonWidth = buttonHeight;
}
// Get the center of the element
var x = e.pageX - posX - buttonWidth / 2;
var y = e.pageY - posY - buttonHeight / 2;
// Add the ripples CSS and start the animation
$(".ripple").css({
width: buttonWidth,
height: buttonHeight,
top: y + 'px',
left: x + 'px'
}).addClass("rippleEffect");
});
here is my fiddle : http://jsfiddle.net/k3AHM/23/
$(document).scroll(function () {
var y = $(this).scrollTop();
if (y > 110) {
$('.menu-container').addClass( "fix-menu" ).animate('top', '-3px');
} else {
$('.menu-container').removeClass("fix-menu");
}
});
now when menu get fixed it's not smooth like this : demo
Any idea ? what's wrong with my code ?
may be you will accept this my update:
https://jsfiddle.net/k3AHM/37/
What I did:
1. You need to check whether function of animation already ran or not (otherwise on every scroll it will be called). This can be done by checking some outer var for 0 or 1 (it will show whether animation ran or not)
2. I used not "animate" function, but slideDown() - I think it's interesting too, though you can use animate instead, of course.
So here's my updated code:
var AlreadyRun=0;
$(document).ready(function(){
$(document).scroll(function () {
var y = $(this).scrollTop();
if (y > 110) {
//$('.menu-container').addClass( "fix-menu" ).animate('top', '-3px');
if(AlreadyRun == 0){
AlreadyRun=1;
//alert('function starts, AlreadyRun='+AlreadyRun);
$('.menu-container').hide().addClass( "fix-menu" ).slideDown('slow');
}
} else {
AlreadyRun=0;
$('.menu-container').removeClass("fix-menu");
}
});
});
and I also think you don't need "transition" in CSS, so I also updated CSS:
.menu-container {
/* transition: all .3s ease-in-out; */
background:red;
margin-top:0;
}
.fix-menu{
/* transition: all .3s ease-in-out;*/
box-shadow: 0 5px 10px 0.5px rgba(0, 0, 0, 0.1);
height: 54px;
left: 0;
top:0;
overflow: hidden;
position: fixed;
right: 0;
z-index: 1500;
/* transition: all 0.2s ease-in; */
}
Hope it is what you needed.
CSS transition solution
http://jsfiddle.net/k3AHM/46/
var fixed = false;
$(document).scroll(function () {
var y = $(this).scrollTop();
if (y > 110) {
if (!fixed)
{
fixed = true;
$('.menu-container').addClass( "fix-menu" );
}
}
else
{
fixed = false;
$('.menu-container').removeClass("fix-menu");
}
});
.menu-container {
transition: top .3s ease-in-out;
background:red;
margin-top:0;
top: -54px;
}
.fix-menu{
box-shadow: 0 5px 10px 0.5px rgba(0, 0, 0, 0.1);
height: 54px;
left: 0;
overflow: hidden;
position: fixed;
right: 0;
z-index: 1500;
top: 0;
}
jQuery animation solution
http://jsfiddle.net/k3AHM/47/
var fixed = false;
$(document).scroll(function () {
var y = $(this).scrollTop();
if (y > 110) {
if (!fixed)
{
fixed = true;
$('.menu-container').addClass( "fix-menu" ).css('top', '-54px').animate({top: '0px'});
}
}
else
{
fixed = false;
$('.menu-container').removeClass("fix-menu");
}
});
.menu-container {
background:red;
margin-top:0;
}
.fix-menu{
box-shadow: 0 5px 10px 0.5px rgba(0, 0, 0, 0.1);
height: 54px;
left: 0;
overflow: hidden;
position: fixed;
right: 0;
z-index: 1500;
}
check this fiddle:
http://jsfiddle.net/k3AHM/24/
.menu-container {
transition: all .3s ease-in-out;
background:red;
margin-top:0;
top:-110px; /* add this */
}
You can add a position to thetop property at the menu-cointeiner and your code works smoothly.