ScrollTrigger not work when direction is horizontal - gsap

I have a sample code, using gsap and Locomotive Scroll
<div data-scroll-container>
<section id="home" class="section">Section home</section>
<section id="about" class="section">Section about</section>
<section id="contact" class="section">Section contact</section>
</div>
And my javascript
gsap.registerPlugin(ScrollTrigger);
const pageContainer = document.querySelector('[data-scroll-container]');
const scroller = new LocomotiveScroll({
el: pageContainer,
smooth: true,
direction: 'horizontal'
});
scroller.on("scroll", ScrollTrigger.update);
const sections = gsap.utils.toArray(".section");
sections.forEach((section, i) => {
ScrollTrigger.create({
trigger: section,
start: () => "top top-=" + (section.offsetLeft - window.innerWidth),
end: () => "+=" + section.offsetWidth,
onEnter: () => {
let section_id = section.getAttribute('id');
console.log("onEnter "+section_id);
},
onEnterBack: () => {
console.log("onEnterBack");
},
onLeave: () => {
console.log("onLeave");
},
onLeaveBack: () => {
console.log("onLeaveBack");
},
});
});
ScrollTrigger.addEventListener("refresh", () => scroller.update());
ScrollTrigger.refresh();
When I scroll wheel mouse, ScrollTrigger not catch event onEnter, onEnterBack...

It looks like you're missing a couple things in order for your Locomotive-Scroll to work (even before factoring in GSAP & ScrollTrigger). I see you've defined the data-scroll-container but the inner elements are missing the data-scroll-section & data-scroll directives.
Compare your code to this snippet from the docs. Your first code block should look like this:
<div data-scroll-container>
<section data-scroll-section id="home" class="section">
<h1 data-scroll>Section home</h1>
</section>
<section data-scroll-section id="about" class="section">
<h1 data-scroll>Section about</h1>
</section>
...
</div>
I'm on a similar journey and find that approaching the problem in smaller, manageable, chunks makes it easier to develop. Start by getting your Horizontal-Scrolling running smoothly then layer-in GSAP tweened animations (still working on this last bit).
The reason ScrollTrigger isn't working is because the scrolling is controlled by Locomotive-Scroll now + it's horizontal. So the issue is essentially that the scroll has been hijacked & ScrollTrigger doesn't know when to trigger animation on the timeline).
I suggest reading the docks related to ScrollTrigger.scrollerProxy(). There's also a great working demo to base yourself on. Worked for me!

Related

How do I smoothly reduce a divs height to 0 in VueJS?

I've tried and failed for hours now. The way I've done it is in onMounted() I first div = document.GetElementByID. Then, div.style.height = '0';. However, since this is being run from inside onMounted(), it doesn't animate or transition it, even though I have all the necessary Tailwind classes for it to do so (note that it successfully resizes the div to 0 height, but there's no animation or transition). I even tried to put the div.style.height = '0' into a seperate function outside onMounted() which didn't work. The only thing that animates or transitions is opacity. And only that. Why? Why not height or anything else? What do I have to do to make it work?
<template>
<div v-if="props.state == 2" class="w-full bg-[#E7685D] text-black text-2xl overflow-hidden duration-500 ease-in-out" id="alert">
<p class="text-center p-2">text</p>
</div>
</template>
<script>
import { onMounted } from '#vue/runtime-core'
onMounted(() => {
const alert = document.getElementById('alert');
setTimeout(() => { alert.style.height = '0%', 5000 })
})
const props = defineProps({
state: String
})
</script>
Maybe that's not animating because of your wrong syntax for setTimeout function. setTimeout(() => { alert.style.height = '0%', 5000 }). You've to define milliseconds out of the function brackets {...} in case you want a 5 seconds delay before execution of the setTimeout function. Given below is the right syntax for the setTimeout function.
Syntax: setTimeout(function, milliseconds, param1, param2, ...)
setTimeout(() => {
alert.style.height = '0%'
}, 5000)
Edited:
I'm not sure! Maybe adding the transition/animation classes in the setTimeout will do the trick.
setTimeout(() => {
alert.classList.add("duration-500", "ease-in-out")
alert.style.height = '0%'
}, 5000)
If it didn't work, try moving lines up/down in the setTimeout function.

Vuejs conditional rendering v-if

I have this part of code wherein in my view theres a condition if I want to show it or not
<div v-if="toShow" ref="target"></div>
and in my javascript code I trigger toShow to true
this.toShow = true
this.$refs.target // always null
but when I use setTimeout() the value is not null
I need a solution wherein I dont want to use setTimeout() because I'm toggling toShow everytime for my transition so what happens is a have a lot of nested setTimeout() in my code.
You can use $nextTick which waits until the next DOM update cycle. It should be much better than setTimeout because it gets called quickly after the DOM updates rather than a specific time later.
I've created a fiddle below showing it working.
new Vue({
el: "#app",
data: () => {
return {
show: false
};
},
methods: {
toggleShow() {
this.show = !this.show;
console.log(this.$refs.target);
this.$nextTick(() => {
console.log(this.$refs.target);
});
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="toggleShow">Toggle Show</button>
<div v-if="show">
<h5>Showing</h5>
<div ref="target"></div>
</div>
</div>

Issue when using v-bind to update star value in semantic ui using vue.js rendering

I am using semantic-ui and vue.js and below is a simple code in which I am running a vue.js for-loop which is fetching merchant, clicked and rating values. The code is working fine except for {{item.rating}}. The thing is although I am getting the numeric value for my rating but somehow it is not updating my "ui star rating" (please see the screenshot. It looks like it is not updating the default value of "initialRating" which is 3 to the value which I want to push. Not sure how to resolve this.
<div id="vue-app" class="ui four doubling cards">
<div id="myList" class="ui small card" v-for="(item, index) in infox" v-show="item.Title">
<p class="small meta">{{ item.Merchant }}</p>
<p class="meta"><i class="mouse pointer icon"></i>{{item.Clicked}} clicked today</p>
<div class="ui star rating" v-bind:data-rating="{{item.Rating}}"></div>
</div>
<script>
$('.ui.rating')
.rating({
initialRating: 3,
maxRating: 5});
</script>
Screenshot of my issue
This issue is now resolved. Apparently what I did was that i inserted the semantic ui's ".ui.rating" function in my vue method just after the axios api call. So it means that after my axios.get is executed then my ".ui.rating" calls gets initialized everytime when I call loadmore.
new Vue({
el: '#vue-app',
delimiters: ['[[', ']]'],
data: {
dino: d_var,
infox: [],
isHidden: false
},
methods: {
loadmore: function () {
axios.get(this.dino)
.then(response => {
this.infox.push(...response.data);
this.dino = "/api/search/" + this.infox[this.infox.length - 1].lid;
kk = this.infox[this.infox.length - 1].gr;
jj = ("true" == kk);
this.isHidden = jj;
})
.then(function () {
$('.ui.rating')
.rating({
initialRating: 3,
maxRating: 5
});
})
}
},
})

React Motion: simple chaining

I'm using react, mobx and I need to animate things.
I want to do a simple animation chaining: when the first animation ends, the second starts.
As use case think of a section templates: every time the user change the section, the current section fade out and then the next section fade in.
Any hints?
Regardless react-motion or any other library, you can always use onAnimationEnd animation event listener .
With this method you are able to detect when css animation ends on related html element and execute method function.
Then you can choose what to do , for example using setState():
animation1 = () => {
this.setState({
animation1: true
})
}
animation2 = () => {
this.setState({
animation2: true
})
}
animation3 = () => {
this.setState({
animation3: true
})
}
render() {
return (
<div>
<div onAnimationEnd={this.animation1}>
First animation
</div>
<div onAnimationEnd={this.animation2}>
Second animation
</div>
<div onAnimationEnd={this.animation3}>
Third animation
</div>
</div>
)
}

Sluggish render VueJS page with 2000-3000 items

I have an administrator view which can display a list of users. Complete list is about 3000 entries. I have pagination enabled by default to 100 users/page, but the admin has the option to display all of them at once.
When 100 users are displayed, everything is fine, but when all 3k are displayed the page becomes sluggish (e.g. if i try to sort a column, it'll take 3-4 seconds to sort them) and I understand there's not much I can do about the response time. I'm using lodash OrderBy method for sorting.
My question is, is there a way to have VueJS rendering the list asynchronously, so if I click sort by Name, then immediately sort by Level, it will cancel the existing sorting operation and start a new one?
Here's a sample code (it doesn't work obviously, i just made it up to illustrate my point):
<template>
<div class="container">
<div class="row">
<div class="col-3" #click="setSort('name')">
Name
</div>
<div class="col-3" #click="setSort('level')">
Level
</div>
</div>
<div class="row" v-for="u in sortedUsers">
<div class="col-3">
{{u.name}}
</div>
<div class="col-3">
{{u.level}}
</div>
</div>
</div>
</template>
<script>
export default {
data: () => ({
users: []
,orderCol:'name'
,orderDir:'asc'
})
,computed : {
sortedUsers () {
return _.orderBy(this.users,this.orderCol,this.orderDir);
}
}
,methods : {
setSort(col){
this.orderCol = col;
this.orderDir = (this.orderDir=='asc') ? 'desc' : 'asc';
}
}
,mounted () {
this.$post('/getusers')
.then((data) => {//assume data is in this format:
// [{'name':'test1', 'level':'admin'}, {'name':'test2','level':'user'}, and so on]
this.users = data;
});
}
};
</script>
The answer is no. There is no way to render all 3000 items async.
Ordering is fast, rendering is slow. Even though you can speed up ordering by laravel or any other app it will not help you to render quicker. You can't speed up rendering by async as I know.
Some links:
https://github.com/vuejs/vue/issues/441
Try to use short list with for example 100 items + pagination (local, just offset for v-for).
vue-virtual-scroller is also interesting if your list of items has fixed height. (Demo)