I've got a border that i would like to reveal when in the viewport and then stay as the complete line. i've used the following css. Does anyone know some lightweight JS to activate the animation?
.draw-line {
border-left:1px solid rgb(255,0,0);
animation: draw-line 5s;
Animation-fill-mode: forwards
}
#keyframes draw-line {
0% {
height:0
}
50% {
border-left:1px solid rgb(89,0,255)
}
100% {
height:100vh;
border-left:1px solid rgb(255,0,0)
}
}
You can use the Insertion Observer API. This snippet will add the class draw-line to all the viewport-divs when the viewport-div is in the viewport, and removes them when out of the viewport.
if (!!window.IntersectionObserver) {
let viewportDivs = [...document.querySelectorAll(".viewport-div")];
viewportDivs.forEach((div) => {
insertionObserverFunction(div);
});
}
function insertionObserverFunction(div) {
let observer = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.intersectionRatio != 0) {
div.classList.add('draw-line')
} else {
div.classList.remove('draw-line')
}
});
},
{ threshold: 0.51 }
);
observer.observe(div);
}
User the threshold property to adjust when to add the class.
const b = document.querySelector('.start-button');
const d = document.querySelector('.draw-line');
b.addEventListener('click', function() {
b.disabled = true;
d.style.animation = 'draw-line 5s';
});
d.addEventListener('animationend', function() {
d.style.animation = 'unset';
b.disabled = false;
});
body {
margin: 0;
}
.draw-line {
border-left: 10px solid rgb(255, 0, 0);
Animation-fill-mode: forwards
}
#keyframes draw-line {
0% {
height: 0
}
50% {
border-left: 10px solid rgb(89, 0, 255)
}
100% {
height: 100vh;
border-left: 10px solid rgb(255, 0, 0)
}
}
.start-button {
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding: 1em 2em;
}
<div class="draw-line"></div>
<button class="start-button">Start</button>
I'm assuming you are using Vanilla JS?
Could this work?
function isInViewport(element) {
const rect = element.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
This function takes in a DOM element and checks if the element is visible in the viewport. It could be paired with an eventlistener for scroll, if it returns true, add .draw-line to the classlist. If it returns false, remove from classlist.
Check this out
Edit:
Maybe I misunderstood what you wanted. If you want elements to play an animation just once and stay like that when scrolled into view.
Then this works
window.addEventListener('scroll', reveal);
function reveal() {
var reveals = document.querySelectorAll('.reveal');
for(const reveal of reveals) {
var revealTop = reveal.getBoundingClientRect().top;
var revealPoint = 150;
if(revealTop < viewport.height - revealPoint) {
reveal.classList.add('draw-line');
}
}
}
What you do here is add .reveal to every element that you want to animate. You dont need to define the styles for .reveal. It just works as some kind of guiding light on what elements you want to trigger.
RevealPoint represents the Y pixels from top.
When reveal comes into view, the animation class gets added to it and stays that way.
Related
I'm facing a problem with wiggling bottom border pseudo css element, pointer events set to none fixes it but I have decide to go with javascript and create a span on mouse enter event and remove it on mouse leave event. with set time method, I created an acceptable animation like feeling but now back to the same problem WIGGLING. So is there an equivalent code in javascript that mimics pointer events set to none?
<a class="top-link">
<span class="title"> Title </span>
</a>
.border-bottom {
border-bottom: solid 3px #ddd;
content: "";
width: 100%;
position: absolute;
top: 100%;
z-index: 1;
left: 0;
transform: translateY(4px);
transition: ease-in 0.2s;
opacity: 0;
}
document.querySelectorAll(".top-link").forEach((el) => {
el.addEventListener("mouseenter", function () {
el.insertAdjacentHTML("beforeend", '<span class="border-bottom"></span>');
el.childNodes.forEach((e) => {
if (e.className == "border-bottom") {
setTimeout(() => {
e.style.transform = "translateY(0px)";
e.style.opacity = "1";
}, 50);
}
});
});
el.addEventListener("mouseleave", function () {
el.childNodes.forEach((e) => {
if (e.className == "border-bottom") {
setTimeout(() => {
e.style.transform = "translateY(4px)";
}, 25);
setTimeout(() => {
e.style.opacity = "0.4";
}, 50);
setTimeout(() => {
e.style.opacity = "0";
e.remove();
}, 250);
}
});
});
});
Is it possible to a piece of javascript on mobile/tablet devices only? I'd like to do something a bit better than display: none and it makes sense to stop the script from running if it's not required?
Basically I have a custom cursor effect, that is only required when it follows the cursor on desktop with a mouse/trackpad.
This is the script I have:
var cursor = document.querySelector('.cursor-outer');
var cursorinner = document.querySelector('.cursor');
var a = document.querySelectorAll('a');
var moveCursor = true;
var radiusOfCursor = parseInt(getComputedStyle(cursor).getPropertyValue('width')) / 2; // radiusOfCursor = (width_of_cursor / 2).
document.addEventListener('mousemove', function (e) {
var x = e.clientX;
var y = e.clientY;
cursorinner.style.left = x + 'px';
cursorinner.style.top = y + 'px';
if (!moveCursor) return;
cursor.style.marginLeft = `calc(${e.clientX}px - ${radiusOfCursor}px)`;
cursor.style.marginTop = `calc(${e.clientY}px - ${radiusOfCursor}px)`;
moveCursor = false;
setTimeout(() => {
moveCursor = true;
}, 32) // The wait time. I chose 95 because it seems to work just fine for me.
});
/* Centre pointer after stopping */
function mouseMoveEnd() {
cursor.style.marginLeft = `calc(${cursorinner.style.left} - ${radiusOfCursor}px)`;
cursor.style.marginTop = `calc(${cursorinner.style.top} - ${radiusOfCursor}px)`;
}
var x;
document.addEventListener('mousemove', function() {
if (x) clearTimeout(x);
x = setTimeout(mouseMoveEnd, 10);
}, false);
/* End */
document.addEventListener('mousedown', function() {
cursor.classList.add('click');
cursorinner.classList.add('cursorinnerhover');
});
document.addEventListener('mouseup', function() {
cursor.classList.remove('click');
cursorinner.classList.remove('cursorinnerhover');
});
a.forEach(item => {
item.addEventListener('mouseover', () => {
cursor.classList.add('hover');
});
item.addEventListener('mouseleave', () => {
cursor.classList.remove('hover');
});
});
a.forEach((item) => {
const interaction = item.dataset.interaction;
item.addEventListener("mouseover", () => {
cursorinner.classList.add(interaction);
});
item.addEventListener("mouseleave", () => {
cursorinner.classList.remove(interaction);
});
});
* {
cursor: none;
}
.cursor-outer {
border: 2px solid black;
border-radius: 100%;
box-sizing: border-box;
height: 32px;
pointer-events: none;
position: fixed;
top: 16px;
left: 16px;
transform: translate(-50%, -50%);
transition: height .12s ease-out, margin .12s ease-out, opacity .12s ease-out, width .12s ease-out;
width: 32px;
z-index: 100;
}
.cursor {
background-color: black;
border-radius: 100%;
height: 4px;
opacity: 1;
position: fixed;
transform: translate(-50%, -50%);
pointer-events: none;
transition: height .12s, opacity .12s, width .12s;
width: 4px;
z-index: 100;
}
<div class="cursor-outer"></div>
<div class="cursor"></div>
Thanks in advance!
Option #1
You can use something similar to this to determine if a device is touch-enabled:
isTouchDevice = () => {
return ( 'ontouchstart' in window ) ||
( navigator.maxTouchPoints > 0 ) ||
( navigator.msMaxTouchPoints > 0 );
};
This is adapted from Patrick H. Lauke's Detecting touch article on Mozilla.
Then just: if (isTouchDevice()) { /* Do touch screen stuff */}
Option #2
But maybe a pure CSS approach could work better in your situation, like:
#media (hover: none) {
.cursor {
pointer-events: none;
}
}
Option #3
If you don't mind using a third-party library, then Modernizr is really great for detecting things like this in the user's environment. Specifically, Modernizr.pointerevents will confirm if touchscreen is being used.
I'm trying to create a Sprite animation using the following image:
To do so I am using it as a background and am trying to manipulate the background's position when animating. Somehow I can't get it working though - it shows the last frame from the very beginning.
Image: https://i.imgur.com/06vjVVj.png - 30800x1398 and 27 frames
Here's a codepen: https://codepen.io/magiix/pen/MWewdYo
#skull {
position: absolute;
border: 1px solid red;
width: 1140px;
height: 1398px;
background: url("https://i.imgur.com/06vjVVj.png") 1140px 0;
}
const animateSkull = () => {
const interval = 50;
let pos = 30800 / 27;
tID = setInterval(() => {
document.getElementById("skull").style.backgroundPosition = `-${pos}px 0`;
if (pos < 30800) {
pos = pos + 1140;
}
}, interval);
};
If you check (with a console for example), you'll see that your animateSkull function is never called, because your addEventListener does not work. Change it to the following so it will be called (but your animateSkull function has another bug (or maybe your css I didn't checked) so it's not fully working after that but you should be able to fix that easily):
document.addEventListener('DOMContentLoaded', () => {
animateSkull();
});
This should do the work, but the frames in your sprite don't have the same width. So the animation looks buggy. (that is one huge image just for the animation)
const animateSkull = () => {
const interval = 1000;
let pos = -1140;
tID = setInterval(() => {
if (pos > -30800) {
pos -= 1140;
}
document.getElementById("skull").style.backgroundPosition = `${pos}px 0`;
}, interval);
};
animateSkull();
#skull {
position: absolute;
border: 1px solid red;
width: 1140px;
height: 1398px;
background-image: url("https://i.imgur.com/06vjVVj.png");
background-position: -1140px 0;
background-size: cover;
}
</style>
<p id="skull"></p>
This codepen shows my problem: http://codepen.io/PiotrBerebecki/pen/pNvpdG
When the user clicks on the big button the css opacity is reduced to 0. Since I've applied the following rule: transition: opacity 0.5s ease-in-out; the fade out animation is smooth.
I would like to achieve the same smooth transition when the next button fades in.
However for some reason the next button appears suddenly without any transition.
Would you know what causes the issue and how to fix it?
console.clear();
(function() {
// Data for the app
const model = {
buttons: ['tomato', 'blue'],
currentButton: -1
};
// Logic for the app
const controller = {
init: function() {
view.init();
},
getButtonName: function() {
model.currentButton = (model.currentButton + 1) % model.buttons.length;
return model.buttons[model.currentButton];
}
};
// View for the app
const view = {
init: function() {
this.root = document.getElementById('root');
this.showNext();
},
animationDelay: 500,
showNext: function() {
// Get next button name
const buttonName = controller.getButtonName();
// Create button DOM element
const buttonElement = document.createElement('div');
buttonElement.className = 'button';
buttonElement.id = buttonName;
buttonElement.textContent = buttonName;
buttonElement.style.opacity = 0;
// Add event listender for the button
buttonElement.addEventListener('click', event => {
// Reduce opacity
buttonElement.style.opacity = 0;
// Remove the button from DOM
setTimeout(() => {
this.root.removeChild(buttonElement);
}, this.animationDelay + 10);
// Start the function to show next button
setTimeout(() => {
this.showNext();
}, this.animationDelay + 20);
});
// Add button to DOM
this.root.appendChild(buttonElement);
// Show button by increasing opacity
buttonElement.style.opacity = 1;
}
};
// Start the app
controller.init();
}());
#tomato {
background: tomato;
}
#blue {
background: DeepSkyBlue;
}
.button {
transition: opacity 0.5s ease-in-out;
width: 100%;
height: 50vh;
border: solid 3px black;
cursor: pointer;
}
<div id="root"></div>
This should work , Code pen link: http://codepen.io/saa93/pen/gLbvmQ
You would need to add this instead of directly setting opacity to 1
// Show button by increasing opacity
buttonElement.style.opacity = 0;
setTimeout(() => {
buttonElement.style.opacity = 1;
}, this.animationDelay + 20);
Add a class (in the Snippet is .active) add the following:
CSS
.button {
opacity: 0;
transition: opacity 0.5s ease-in-out;
width: 100%;
height: 50vh;
border: solid 3px black;
cursor: pointer;
}
.button.active {
opacity: 1;
transition: opacity 0.5s ease-in-out;
}
JavaScript
...
// Reduce opacity
buttonElement.classList.toggle('active');
buttonElement.style.opacity = 0;
...
// Show button by increasing opacity
buttonElement.classList.toggle('active');
buttonElement.style.opacity = 1;
SNIPPET
console.clear();
(function() {
// Data for the app
const model = {
buttons: ['tomato', 'blue'],
currentButton: -1
};
// Logig for the app
const controller = {
init: function() {
view.init();
},
getButtonName: function() {
model.currentButton = (model.currentButton + 1) % model.buttons.length;
return model.buttons[model.currentButton];
}
};
// View for the app
const view = {
init: function() {
this.root = document.getElementById('root');
this.showNext();
},
animationDelay: 500,
showNext: function() {
// Get next button name
const buttonName = controller.getButtonName();
// Create button DOM element
const buttonElement = document.createElement('div');
buttonElement.className = 'button';
buttonElement.id = buttonName;
buttonElement.textContent = buttonName;
buttonElement.style.opacity = 0;
// Add event listender for the button
buttonElement.addEventListener('click', event => {
// Reduce opacity
buttonElement.classList.toggle('active');
buttonElement.style.opacity = 0;
// Remove the button from DOM
setTimeout(() => {
this.root.removeChild(buttonElement);
}, this.animationDelay + 10);
// Start the function to show next button
setTimeout(() => {
this.showNext();
}, this.animationDelay + 20);
});
// Add button to DOM
this.root.appendChild(buttonElement);
// Show button by increasing opacity
buttonElement.classList.toggle('active');
buttonElement.style.opacity = 1;
}
};
// Start the app
controller.init();
}());
#tomato {
background: tomato;
}
#blue {
background: DeepSkyBlue;
}
.button {
opacity: 0;
transition: opacity 0.5s ease-in-out;
width: 100%;
height: 50vh;
border: solid 3px black;
cursor: pointer;
}
.button.active {
opacity: 1;
transition: opacity 0.5s ease-in-out;
}
<div id="root"></div>
after this.root.appendChild(buttonElement);
you should set opacity to 0 and let the browser time to render before buttonElement.style.opacity = 1;
BTW I think removing and adding the element of not a good way to do this
.button {
width: 100%;
height: 50vh;
border: solid 3px black;
cursor: pointer;
animation-name: example;
animation-duration:3.5s;
}
#keyframes example {
0% {opacity:1}
50% {opacity:0}
100% {opacity:1}
}
What U really want is to use animation like this:JSFIDDLE EXAMPLE
This way the animation does all this timing and opacity back and forth using the css only
I have a blinking red box in my html it uses css animations. I want to to be able to change it from blinking red and white to green and white. I know this can be done on id elements by using getElementbyId but how would get access to the green aminated box in the css.
The red box looks like this:
#-webkit-keyframes 'noConnection'
{
1% { background-color: red; }
33% { background: white; }
66% { background: red; }
100% { background: white; }
}
The green is this:
#-webkit-keyframes 'Connection'
{
1% { background-color: green; }
33% { background: white; }
66% { background: green; }
100% { background: white; }
}
The animate looks like this:
#animate {
height: 15px;
width: 15px;
}
.cssanimations #animate {
-webkit-animation-direction: normal;
-webkit-animation-duration: 5s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-name: Connection;
-webkit-animation-timing-function: ease;
and I think I have to change the attribute -webkit-animation-name: from javascript to do this but I dont know how to get a handle on it to change it.
Or would I be better off creating a duplicate #animate and renaming it using the getElementById?
Here is a simple web page that demonstrates how to use Javascript to modify a CSS animation. It contains a simple div, and a little Javascript causes the div to move randomly around the page.
In your specific case, you just need to touch up the line that calls "insertRule" to insert your new blinking rule.
<html><head><style></style></head>
<body>
<div id="mymovingdiv">
<h1>Moving Div</h1>
<p>And some little text too</p>
</div>
<script>
function animatedMove(id, xStart, yStart, xEnd, yEnd, secs)
{
// Remove any CSS rules inserted by a previous call to this method
let rulename = `AnimMove${id}`;
let ss = document.styleSheets; // all stylesheets
for (let i = 0; i < ss.length; ++i) { // for each stylesheet...
for (let j = ss[i].cssRules.length - 1; j > 0; j--) { // for each rule...
if (ss[i].cssRules[j].name === rulename) { // does the name match?
ss[i].deleteRule(j);
}
}
}
// Insert a CSS rule for this animation
document.styleSheets[0].insertRule(`#keyframes ${rulename} { 0% { left: ${xStart}px; top: ${yStart}px; } 100% { left: ${xEnd}px; top: ${yEnd}px } }`);
// Remove any CSS rules inserted by a previous call to this method
for (let i = 0; i < ss.length; ++i) { // for each stylesheet...
for (let j = ss[i].cssRules.length - 1; j > 0; j--) { // for each rule...
if (ss[i].cssRules[j].name === rulename) { // does the name match?
ss[i].deleteRule(j);
}
}
}
// Insert a CSS rule for this animation
document.styleSheets[0].insertRule(`#keyframes ${rulename} { 0% { left: ${xStart}px; top: ${yStart}px; } 100% { left: ${xEnd}px; top: ${yEnd}px } }`);
// assign the animation to our element
let el = document.getElementById(id);
el.style.position = 'absolute';
el.style.animation = `${rulename} ${secs}s`;
// Make the element stay where the animation ends
el.style.left = `${xEnd}px`;
el.style.top = `${yEnd}px`;
// Re-clone the element, to reset the animation
el.parentNode.replaceChild(el.cloneNode(true), el);
}
let x = 0;
let y = 0;
function randomMove()
{
let newX = Math.floor(Math.random() * 800);
let newY = Math.floor(Math.random() * 600);
animatedMove('mymovingdiv', x, y, newX, newY, 1);
x = newX;
y = newY;
}
setInterval(randomMove, 1000);
</script>
</body>
</html>
The easiest way to do this is to created two classes in CSS and then toggle between the two.
So something like:
.connection {
animation: 'Connection' 5s ease infinite
}
.no-connection {
animation 'noConnection' 5s ease infinite
}
And then use Javascript to toggle between the two
function toggleConnectionStatus(){
var element = document.getElementById('animate');
if(/* connection is active */) element.className = 'connection';
else element.className = 'no-connection'
}