How to delete image from array if display="none" using Vue.js? - javascript

I am using Vue.js in my project. I have a background of images which are animated, they are moving from up to down. Everything connected to the random image, position and etc is inside created():
const vue = require("#/assets/images/vue.png");
const bootstrap = require("#/assets/images/bootstrap.png");
const bulma = require("#/assets/images/bulma.png");
export default {
name: "randImg",
data() {
return {
images: [
vue,
bootstrap,
bulma
],
addedImage: [],
imgTop: -100,
imgLeft: -100,
imgHeight: 64,
imgWidth: 64,
changeInterval: 250
}
},
created() {
const randomImg = func => setInterval(func, this.changeInterval);
randomImg(this.randomImage);
randomImg(this.addImage);
randomImg(this.randomPosition);
},
mounted: function () {
if (this.addedImage[i] = {
style: {display: `none`}
}) {
this.addedImage.remove(this.addedImage[i]);
}
},
methods: {
randomImage() {
const idx = Math.floor(Math.random() * this.images.length);
this.selectedImage = this.images[idx];
},
randomPosition() {
const randomPos = twoSizes => Math.round(Math.random() * twoSizes);
this.imgTop = randomPos(screen.height / 10 - this.imgHeight);
this.imgLeft = randomPos(screen.width - this.imgWidth);
},
addImage() {
if (this.addedImage.length > 500) {
this.addedImage.splice(0, 300);
} else {
this.addedImage.push({
style: {
top: `${this.imgTop}px`,
left: `${this.imgLeft}px`,
height: `${this.imgHeight}px`,
width: `${this.imgWidth}px`
},
src: this.selectedImage
});
}
}
}
}
css
.image {
position: fixed;
z-index: -1;
opacity: 0;
animation-name: animationDrop;
animation-duration: 5s;
animation-timing-function: linear;
animation-iteration-count: 1;
filter: blur(3px);
will-change: transform;
}
#keyframes animationDrop {
15% {
opacity: 0.2;
}
50% {
opacity: 0.4;
}
80% {
opacity: 0.3;
}
100% {
top: 100%;
display: none;
}
}
and html
<div class="randImg">
<img class="image" :style="image.style"
:src="image.src"
v-for="image in addedImage">
</div>
My site is lagging because images are adding to the DOM infinitely. The idea of my animation is that when my image is on keyframe 100% it will have display none. So I decide to simply create an if statement inside mounted() but it doesn't work; I get the error message "ReferenceError: i is not defined".
How can I delete the images when their display become none?

You want each image to persist for five seconds (based on your animation-duration), and you're adding one image every 250ms (based on your changeInterval variable). This means that your image array needs to contain a maximum of twenty images, rather than the 500 you're currently limiting it to.
You could control this by modifying your addImage function to drop the oldest image before adding the newest one. (You're already doing this, except that you're waiting until five hundred of them have built up and then splicing off three hundred at once; better to do it one at a time:)
addImage() {
if (this.addedImage.length > 20) {
this.addedImage.shift() // remove oldest image (the first one in the array)
}
// add a new image to the end of the array:
this.addedImage.push({
style: {
top: `${this.imgTop}px`,
left: `${this.imgLeft}px`,
height: `${this.imgHeight}px`,
width: `${this.imgWidth}px`
},
src: this.selectedImage
});
}
There's no need to read the display value from the DOM, you can just depend on the timing to ensure that the images aren't removed before they're supposed to; modifying the array is all that's necessary to remove the elements from the DOM. (You could harmlessly leave a few extra in the array length, just in case, but there's no need to go all the way to 500.) mounted() wouldn't be useful for this case because that function only runs once, when the component is first drawn onto the page, before there's anything in the addedImages array.

Using v-if to remove images which style is display: none. And i think you can
delete all code in mounted().
<div class="randImg">
<template v-for="image in addedImage">
<img v-if="image.style.display !== 'none'" class="image"
:style="image.style" :src="image.src">
</template>
</div>

Related

Speed up setInterval() for one revolution of an infinite carousel (React)

I'm trying to build an infinite carousel for a webpage. I've managed to get it to work quite well, but there is one small bug which is annoyingly noticeable. Here is the setup:
I have each of the images in an array and I have both an activeIndex and a looping variable in the state. The image array has three clones at the end of it ([...imgArray, imgArray[0], imgArray[1], imgArray[2]]) as 3 images are visible at all times.
I use a useEffect() and setInterval() to update the active index every 2 seconds and if the index is the second from the last item in the array, it sets looping to true.
Then, down in the rendering components, I am dynamically changing the transform: translateX() to move the images and when looping is true it sets transition: none.
The problem is, when the carousel loops back to the beginning, it takes twice as long for that image to move again because it uses two rounds of the setInterval(). I wanted to just make it loop back to the next image, but because there are more animations in there, it doesn't seem possible to me.
My next attempt was to make the setInterval have a variable time, but I can't get that to work either.
Let me know if anybody has any ideas. Please and thank you in advance!
Here is what the code looks like:
// Carousel.tsx
useEffect(() => {
const interval = setInterval(() => {
setActiveIndex(prev => {
setLooping(false)
if (prev + 1 >= carouselArray.length - 1) {
setLooping(true)
return 1;
}
return prev + 1
});
}, 2000)
return () => {
if (interval) clearInterval(interval)
}
}, [carouselArray.length])
return (
<div className={style.carousel}>
<div
className={style.inner}
style={!looping ?
{ transform: `translateX(-${(1 / 3 * 100) * (activeIndex - 1)}%)` } :
{ transition: 'none' }}
>
{carouselArray.map((img, i) =>
<div
className={style.video}
style={
i === activeIndex ? !looping ?
{ margin: '0 0.3rem', opacity: 1, transform: 'scale(100%)' } :
{ transition: 'none' } :
{ opacity: '50%', transform: 'scale(85%)' }}
>
<ImgPreview />
</div>
)}
</div>
</div>
)
// Carousel.module.scss
.carousel {
width: 100%;
overflow-x: hidden;
margin: 0 auto 1rem;
max-width: 800px;
.inner {
display: flex;
transition: transform 0.5s ease;
.video {
flex: 1 0 calc(100% / 3);
transition: all 0.5s ease;
video {
width: 100%;
}
}
}
}

Add css keyframe animation every image switch

I have an image tag where in every 3 seconds it changes to a different image and I want to add into it an animation style where every time the image switch, a css animation keyframe will take effect. It seems that I cant figure out how to apply the animation in javascript. Here's what Ive tried so far:
My javascript,:
let indexObj = {
imageChange: document.getElementById("imageChanger"), //id of the image tag
imagePath: ["images/Ps_Logo.png", "images/Pencil.png"],
indexPos: 0,
ChangeImage: () => {
setInterval(() => {
indexObj.imageChange.src = indexObj.imagePath[indexObj.indexPos];
indexObj.imageChange.classList.add("imageToChange"); // imageToChange is the name of the css class where the animation is written on.
indexObj.indexPos++;
if (indexObj.indexPos === 2) {
indexObj.indexPos = 0;
}
}, 3000)
}
}
indexObj.ChangeImage();
Here is my css code. The class and animation keyframe:
.imageToChange {
height: 55px;
width: 70x;
border-radius: 20%;
animation-name: animateImage;
animation-duration: .5s;
animation-fill-mode: forwards;
}
#keyframes animateImage {
0% {
margin-top: 2px;
opacity: 0;
}
50% {
margin-top: 7;
}
100% {
opacity: 1;
margin-top: 9px;
}
}
The animation only runs the first time you add the .imageToChange class (and it does work with your code).
You need to either remove the class after the animation or switch to a different class each time you iterate an image.
Have you tried adding and removing (toggling on and off) on your animateImage class? Consider adding the animation class and then toggling it after the animation is completed.
You could use the addClass("imageToChange") and removeClass("imageToChange") jQuery methods: https://api.jquery.com/addClass/
Or using Vanilla JS toggle a class: https://www.w3schools.com/howto/howto_js_toggle_class.asp
Just be sure to retrigger the event the desired number of times.

How to set a css animation class variable with Javascript/jQuery?

I've been implementing some CSS animations as named classes so I can easily add/remove any associated animation for an element, making it "available" for subsequent or repeat animations.
I'm dipping my toes into using CSS variables and it's currently throwing me for a loop. I'm trying to allow the user to rotate an active image in 90 degree increments. In the code example below, I'm showing only the positive 90 button click event.
*.scss
:root {
--rotation-degrees: 90;
}
#keyframes rotate {
100% {
transform: rotate(var(--rotation-degrees)+'deg');
}
}
.animation-rotate {
--rotation-degrees: 90;
// NOTE: I suspect the variable does not need to be supplied here, removing does
// not fix the issue, at least in isolation
animation: rotate(var(--rotation-degrees)) 0.2s forwards;
}
*.js
let degrees = 0;
function rotate(degrees_increment) {
degrees += degrees_increment;
// The use of document.documentElement.style.setProperty is something I've seen
// used in many of the articles I've read as a means to "get to" the css variable,
// so I'm simply blindly copying it's use here
document.documentElement.style.setProperty('--rotation-degrees', degrees +'deg');
$('#main-image-slider img').addClass('animation-rotate');
}
$('#rotate-right-button').on('click', function(event) {
event.preventDefault();
rotate(90);
});
Thank you in advance for any insights and help you can give!
I don't think it's possible to concatenate a CSS variable with a string as you were trying in your CSS:
transform: rotate(var(--rotation-degrees)+'deg');
It's better to handle that with JavaScript.
I think the main issue you're having is that the class needs to be removed after the animation has run (in order for it to be able to run again). You can do that with the animationend event listener.
Demo below:
const DIV = $('div');
const BUTTON = $('#rotate-right-button');
const SPAN = $('#variable-value');
const PROPERTY_NAME = '--rotation-degrees';
const DEG = 'deg';
const ROOT = document.documentElement;
const ElementClass = {
ROTATE_ANIMATION: 'animation-rotate'
}
const ROTATION_VALUE = 90;
const Event = {
CLICK: 'click',
ANIMATION_END: 'animationend'
}
let degrees = 0;
function rotate(degrees_increment) {
degrees += degrees_increment;
ROOT.style.setProperty(PROPERTY_NAME, degrees + DEG);
SPAN.html(`${PROPERTY_NAME}: ${degrees + DEG}`);
}
BUTTON.on(Event.CLICK, function() {
DIV.addClass(ElementClass.ROTATE_ANIMATION);
DIV.on(Event.ANIMATION_END, function(event) {
$(this).removeClass(ElementClass.ROTATE_ANIMATION);
});
rotate(ROTATION_VALUE);
});
#keyframes rotate {
100% {
transform: rotate(var(--rotation-degrees));
}
}
.animation-rotate {
animation: rotate 0.2s;
}
div {
background: red;
width: 100px;
height: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div></div>
<button id="rotate-right-button">rotate</button>
<br>
<span id="variable-value"></span>
I think the highlighted code is not triggering the animation again when adding a class -
Existing Code
var angle = 0;
$("#rotate").click(function(e){
angle+= 90;
document.documentElement.style.setProperty('--deg', angle+'deg');
var el = $(".square");
var newone = el.clone(true);
el.before(newone);
$(".square:last").remove();
});
:root {
--deg: 180deg;
}
#keyframes rotate {
100% {transform: rotate(var(--deg));}
}
.animation-rotate {
animation: rotate 0.9s forwards;
}
.square {
background: hsl(300, 90%, 52%);
width: 15vmin;
height: 15vmin;
border-radius: 10%;
margin: 25px;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="square animation-rotate"></div>
<div>
<button class="btn btn-sm btn-success" id="rotate">Rotate</button>
</div>
</div>
I'm not sure I understand your code, but here's my take on it.
When the user clicks, you can either:
- add/remove a CSS class. In that case the values for the rotation are already set in your CSS.
- rotate the element directly in JavaScript, for instance by dynamically setting a CSS rule.
Seems to me like you're doing both at the same time?

how to animate image in react when image change after some interval?

I am trying to make a image slider in react in which a image is change after 5000 second.
I checked from here http://mumbaimirror.indiatimes.com/ .where this website implement that functionality .
I tried to implement same this in react .I am able to make that , but my image is not slide(from right to left) in other words image not showing animation when second image show n view
here is my code
https://codesandbox.io/s/YrO0LvAA
constructor(){
super();
this.pre=this.pre.bind(this);
this.next=this.next.bind(this);
this.state ={
currentSlide :0
}
setInterval(()=>{
var current = this.state.currentSlide;
var next = current + 1;
if (next > this.props.stories.items.length - 1) {
next = 0;
}
this.setState({ currentSlide: next });
}, 5000);
}
One way to do this is to always have the future image (next image) be ready on the right side so you can transition it to the left, at the same time you transition the current one to the left. So they both move together.
In React this would mean that you would need to store the Index of both the current image and your next image, and each X seconds you need to move them both to the left (or the right depending on your action)
Here's a proof of concept:
https://codepen.io/nashio/pen/xLKepZ
const pics = [
'https://cdn.pixabay.com/photo/2017/06/19/07/12/water-lily-2418339__480.jpg',
'https://cdn.pixabay.com/photo/2017/07/18/18/24/dove-2516641__480.jpg',
'https://cdn.pixabay.com/photo/2017/07/14/17/44/frog-2504507__480.jpg',
'https://cdn.pixabay.com/photo/2016/09/04/13/08/bread-1643951__480.jpg',
];
class App extends React.Component {
constructor(props) {
super(props);
const idxStart = 0;
this.state = {
index: idxStart,
next: this.getNextIndex(idxStart),
move: false,
};
}
getNextIndex(idx) {
if (idx >= pics.length - 1) {
return 0;
}
return idx + 1;
}
setIndexes(idx) {
this.setState({
index: idx,
next: this.getNextIndex(idx)
});
}
componentDidMount() {
setInterval(() => {
// on
this.setState({
move: true
});
// off
setTimeout(() => {
this.setState({
move: false
});
this.setIndexes(this.getNextIndex(this.state.index));
}, 500); // same delay as in the css transition here
}, 2000); // next slide delay
}
render() {
const move = this.state.move ? 'move' : '';
if (this.state.move) {
}
return (
<div className="mask">
<div className="pic-wrapper">
<div className={`current pic ${move}`}>
{this.state.index}
<img src={pics[this.state.index]} alt="" />
</div>
<div className={`next pic ${move}`}>
{this.state.next}
<img src={pics[this.state.next]} alt="" />
</div>
</div>
</div>
);
}
}
React.render(<App />, document.getElementById('root'));
// CSS
.pic {
display: inline-block;
width: 100px;
height: 100px;
position: absolute;
img {
width: 100px;
height: 100px;
}
}
.current {
left: 100px;
}
.current.move {
left: 0;
transition: all .5s ease;
}
.next {
left: 200px;
}
.next.move {
left: 100px;
transition: all .5s ease;
}
.pic-wrapper {
background: lightgray;
left: -100px;
position: absolute;
}
.mask {
left: 50px;
overflow: hidden;
width: 100px;
height: 120px;
position: absolute;
}
EDIT: Updated the POC a bit to handle left and right navigation, see full thing HERE
This is by no means an elegant solution, but it does slide the image in from the left when it first appears: https://codesandbox.io/s/xWyEN9Yz
I think that the issue you're having is because you are only rendering the current story, but much of your code seemed to assume that there would be a rolling carousel of stories which you could animate along, like a reel.
With the rolling carousel approach it would be as simple as animating the left CSS property, and adjusting this based on the currently visible story. I.e. maintain some state in your component which is the current index, and then set the 'left' style property of your stories container to a multiple of that index. E.g:
const getLeftForIndex = index => ((index*325) + 'px');
<div className="ImageSlider" style={{ left: getLeftForIndex(currentStory) }}>
<Stories />
</div>

Javascript degradation solutions for CSS3 animations

I've been creating a number of small thick client JavaScript apps for an iPad app, which loads the relevant app in a UIWebview. I am now making them cross browser and need to incorporate some fallbacks for CSS animations and transitions using JavaScript.
My webkit specific implementation uses CSS classes for all animations/transitions, for which the start and end states are known at design time, using add/remove class in javascript and utilising the relevant webkitTransitionEnd/webkitAnimationEnd events.
For 'dynamic' transitions I have a simple 'animate' function which just applies styling properties to relevant elements.
I would like to keep the internal API for appling transitions as similar as possible to the current implementation by simple adding/removing classes etc. I generally have a CSS and js file (both minified) for an app.
So a few questions/points that I would appreciate any input on:
IE7/8 issues - IE9.js
Dynamically adding vendor specific prefixes - so far found 'jquery.css3finalize'.
Transitioning to a class: 'jquery.animateToClass' - seems to search stylesheet(s) every time a class is applied - should relevant classes be cached on further lookups? Is this slow/resource hungry?
For '#keyframe' animations: I'd like to have a javascript object 'representation' of keyframes of each CSS3 animation. Therefore passing a class to the 'doAnimationWithClass' function would use normal css3 animation if available in the browser but if not it would pass the 'object version' to a function that would chain the each key frame transition using css3 transitions (if available) or jQuery.animate (or equivalent), ultimately arriving at the same result.
So for instance:
CSS:
#-webkit-keyframes an-animation {
0% { opacity: 0; }
50% { opacity: 1; }
100% { opacity: 0; }
}
.an-animation {
-webkit-animation-name: an-animation;
-webkit-animation-duration: 1s;
-webkit-animation-timing-function: linear;
-webkit-animation-iteration-count: 2;
}
JS:
var animations = {
an-animation: {
keyframes: [
{
duration: '',
timing: 'linear',
props: {
opacity: 0
}
},
{
duration: '0.5s',
timing: 'linear',
props: {
opacity: 1
}
},
{
duration: '0.5s',
timing: 'linear',
props: {
opacity: 0
}
}
]
}
};
var animationClasses = [
an-animation: {
name: 'an-animation';
duration: '1s';
timing: 'linear';
count: 2;
}
];
function doAnimationWithClass(className) {
if (modernizer.cssanimations) {
//normal implementation
}
else {
//lookup class in animationclasses
//lookup relevant animation object in animationHash
//pass to animation chaining function
}
}
The creation of animationHash etc could be done client side, or simply created at design time (with a batch/build file).
Any thoughts or pointers to a library that already does this in a more sensible way would be appreciated.
Yes, that's right. You need to transfer keyframes setting to js object. I think css animation and keyframe is a better way to write animation. JS animation way is hardly to maintain. Here is my workaround solution. And I also write a small tool for translating css keyframes to js object(css keyframes to js object) .
var myFrame = {
'0%': {
left: 0,
top: 0
},
'25%': {
left: 100,
top: 100
},
'50%': {
left: 0,
top: 300
},
'100%': {
left: 0,
top: 0
}
};
var keyframes = {
set: function($el, frames, duration) {
var animate;
animate = function() {
var spendTime;
spendTime = 0;
$.each(frames, function(idx, val) {
var stepDuration, stepPercentage;
stepPercentage = idx.replace('%', '') / 100;
stepDuration = duration * stepPercentage - spendTime;
$el.animate(val, stepDuration);
return spendTime += stepDuration;
});
return setTimeout(animate, duration);
};
return animate();
}
};
keyframes.set($('#mydiv'), myFrame, 2000);
demo: http://jsbin.com/uzodut/4/watch
Here are some important pointers http://addyosmani.com/blog/css3transitions-jquery/
Blackbingo, you answer served to me perfectly. I added the easing option, to implement a jquery fallback for ie8 in a parallax starfield (see CSS parallax background of stars) like this:
var animations = {
'STAR-MOVE': {
'0%': {
'background-position-x': '5%',
'background-position-y': '5%'
},
'100%': {
'background-position-x': '1300%',
'background-position-y': '600%'
}
}
};
var keyframes = {
set: function($el, frames, duration, easing) {
var animate;
animate = function() {
var spendTime;
spendTime = 0;
$.each(frames, function(idx, val) {
var stepDuration, stepPercentage;
stepPercentage = idx.replace('%', '') / 100;
stepDuration = duration * stepPercentage - spendTime;
$el.animate(val, stepDuration, easing);
return spendTime += stepDuration;
});
return setTimeout(animate, duration);
};
return animate();
}
};
keyframes.set($('.midground'), animations['STAR-MOVE'], 150000,'linear');
keyframes.set($('.foreground'), animations['STAR-MOVE'], 100000,'linear');

Categories