Hi I use gsap and bootstrap
Each version is gsap 3.9.1 and bootstrap 5.1.3
My problem is that the end of the scroll trigger does not work properly.
const navbarani = gsap.from('.navbar', {
yPercent: -100,
paused: true,
duration: 0.5
}).progress(1)
ScrollTrigger.create({
start: 'top top',
end: 'bottom',
onUpdate: (self) => {
self.direction === -1 ? navbarani.play() : navbarani.reverse()
}
})
If I change end: 'bottom' to end: 999999 it works properly, but
I can't use end: 999999 because I want the scroll to work only when it's at the top.
What I don't understand is that if i erase the bootstrap from the cdn, end: 'bottom' works well.
That's why I think bootstrap and gsap scroll trigger's end cause conflict.
This is the code that I experimented with.
<template>
<div class="navbar">I'm navbar</div>
<div class="blank" />
</template>
<script>
import { onMounted } from 'vue'
import { gsap } from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
gsap.registerPlugin(ScrollTrigger)
export default {
setup () {
onMounted(() => {
const navbarani = gsap.from('.navbar', {
yPercent: -100,
paused: true,
duration: 0.5
}).progress(1)
ScrollTrigger.create({
start: 'top top',
end: 'bottom',
onUpdate: (self) => {
self.direction === -1 ? navbarani.play() : navbarani.reverse()
}
})
})
}
}
</script>
<style>
.navbar {
position: fixed;
}
.blank {
height: 150vh;
}
</style>
I wonder if this collision only occurs to me or if the version is wrong.
If you don't have enough explanation, please let me know. I'll add an explanation.
Thank you for your help.
Related
I am trying to imply some animations in an image while scrolling , the image will grow while scrolling down and return to normal when scrolling up . The reference : codepen.
This is my react code :
<ArticleWrapper ref={ref}>
<img id='grow' className='image' src={Img} alt='Image' />
</ArticleWrapper>
I have tried to implement this but it didnt work when I scroll :
const ref = useRef(null);
useEffect(() => {
const element = ref.current;
scrollTrigger: {
trigger: "#grow",
scrub: 1.5,
start: "top center",
end: "+=400",
ease: "power1.out"
},
{
duration: 1,
scale: 1
}
);
}, []);
Any idea how to do that ?
I have a react paper plane icon that I would like to make a cool effect by making it travel around the HTML document reaching the final position which is the menu button at the top.
This is how my react component called NavPlane looks like:
import React, { Component, useEffect } from "react";
import { useCycle } from "framer-motion";
import { FaPaperPlane } from "react-icons/fa";
import { motion } from "framer-motion";
const PlaneVariants = {
animationOne: {
x: 370,
y: 250,
transition: { ease: [0.17, 0.67, 0.83, 0.67] },
},
animationTwo: {
x: 140,
y: 150,
transition: { duration: 1.0 },
},
animationThree: {
x: 170,
y: 200,
transition: { duration: 1.0 },
},
};
export default function NavPlane() {
const [animation, cycleAnimation] = useCycle(
"animationOne",
"animationTwo",
"animationThree"
);
useEffect(() => {
setTimeout(() => {
cycleAnimation();
}, 1000);
}, []);
return (
<motion.div
className="text-2xl text-gray-600"
variants={PlaneVariants}
animate={animation}
>
<FaPaperPlane />
</motion.div>
);
}
cycleAnimation() was supposed to cycle through 3 animations but only cycling through the first two. The 3rd is only applied when making some change to the code and updating it.
The final goal is to have a movement that goes from right corner of the page to middle, does a knot movement and then travels to the top of the page.
Any ideas how to make it cycle through as many animation variants as I want?
cycleAnimation only advances one step. Initially it will start "animationOne". Once you call cycleAnimation() after 1 second, "animationTwo" will start. You need another timer if you want to play "animationThree"
useEffect(() => {
setTimeout(cycleAnimation, 1000); // start "animationTwo" after 1 second
setTimeout(cycleAnimation, 2000); // start "animationThree" after 2 seconds
}, []);
I'm quite new to Vue.js and have had some problems getting libraries to work without getting the "error 'X' is not defined no-undef" message.
In this case it is 'Back' that is not defined (which is a part of GSAP)
I figured the only place to "define" Back would be in the import.
Is this just the way to import libraries?
Do I have to write every undefined part in the import like this?
It works but it just seems unnecessary.
<template>
<div id="mainTemplate">
<h2>This is the MainTemplaye.vue Component</h2>
<div ref="box" class="box"></div>
</div>
</template>
<script>
import { TimelineLite, Back } from "gsap";
export default {
name: "MainTemplate",
mounted() {
const { box } = this.$refs;
const timeline = new TimelineLite();
timeline.to(box, 1, { x: 200, rotation: 90, ease: Back.easeInOut, })
timeline.to(box, 0.5, { background: 'green' },'-=0.5')
},
};
</script>
<style>
.box {
height: 60px;
width: 60px;
background: red;
}
</style>
I'm not sure where you're learning from, but you're using the old syntax of GSAP. If you use the new syntax of GSAP you don't have to import anything other than gsap in your case:
import { gsap } from "gsap";
export default {
name: "MainTemplate",
mounted() {
const { box } = this.$refs;
const timeline = gsap.timeline();
timeline.to(box, { duration: 1, x: 200, rotation: 90, ease: 'back.inOut' })
timeline.to(box, { background: 'green' }, '-=0.5')
},
};
The best place to start learning is the official GSAP Getting Started article.
First, the terms, "link" is the area where the mouse enters. The "tooltip" is the thing that pops up and shows extra information.
--- above added 2020-04-29
I'm using Vuetify and trying to keep the v-tooltip open when mouse is hovering over the "tooltip".
The content inside the tooltip is going to be rich and don't want that to automatically hide when visitor is looking into it.
<template>
<v-tooltip
v-model="show"
max-width="600px"
content-class="link-tooltip-content"
bottom>
<template v-slot:activator="{ on }">
<div
:style="boxStyle"
#mouseover="mouseover"
#mouseleave="mouseleave"
></div>
</template>
<template v-slot:default>
<v-card
#mouseover="mouseover"
#mouseleave="mouseleave"
>
<v-row dense>
<v-col>
<v-card-title class="headline">
rich tooltip
</v-card-title>
</v-col>
</v-row>
</v-card>
</template>
</v-tooltip>
</template>
<script>
export default {
data: () => ({
show: false,
hoverTimer: null
}),
methods: {
boxStyle: function() {
return {
left: "100px",
top: "100px",
width: "100px",
height: "100px",
position: "absolute"
};
},
mouseover: function() {
console.log(`mouseover`);
this.show = true;
clearTimeout(this.hoverTimer);
},
mouseleave: function() {
console.log(`mouseleave`);
this.hoverTimer = setTimeout(() => {
this.show = false;
}, 3000);
}
}
};
</script>
But this doesn't work. The mouseover and mouseleave event handlers on the activator slot (the "link") element does fire, but the event handlers on the default slot (the "tooltip") don't fire.
I think the reason is, because the content inside the "tooltip" is moved to somewhere else under the body tag.
The questions is, how can I keep the "tooltip" open when hovering over it.
I'm moving the mouse like this:
Hover over the link (the tooltip shows up).
Move the mouse out of the link and into the tooltip. (The link and tooltip is a few pixels apart)
Now the mouseleave event for the link fires, and I want to add a mouseenter event handler on the tooltip. How do I do that ?
I'm thinking to add an mouseenter event on the tooltip, so that I can clearTimeout(hoverTimer) and keep the tooltip open.
I know there's a similar question from 9 years ago, using jQuery Keep tooltip opened when the mouse is over it , but I don't want to use jQuery if possible. I prefer a Vue way.
Here's a little reproducible example:
https://www.codeply.com/p/GuFXqAAU8Y
Instead of using v-tooltip, I suggest you use v-menu with the open-on-hover props set to true.
If you have to nudge whatever you put in the menu, make sure to set an appropriate close-delay value, so the menu doesn't close before the user reaches it.
Example:
https://codepen.io/stephane303/pen/WNwdNxY
<v-menu open-on-hover right offset-x nudge-right="20" close-delay="100">
.v-tooltip__content has pointer-events:none set in vuetify.min.css. If you set it back to auto you allow it to be hovered.
When its hovered, its parent is hovered. And when its parent is hovered, it has a tooltip. So all you need is:
.v-tooltip__content {
pointer-events: auto;
}
I made extended version of VTooltip for this purpose. Just pass interactive prop. See working example here by hovering Governance stepper: https://tzkt.io
<script>
/**
* Extends VTooltip with interactivity
* #see https://material-ui.com/components/tooltips/#interactive
*/
import { VTooltip } from 'vuetify/lib';
export default {
extends: VTooltip,
props: {
interactive: {
type: Boolean,
default: false,
},
closeDelay: {
type: [Number, String],
default: 50,
},
},
computed: {
// I'm not 100% sure in this, but it works
calculatedLeft() {
const originalValue = VTooltip.options.computed.calculatedLeft.call(this);
if (!this.interactive) return originalValue;
const { left, right } = this;
let value = parseInt(originalValue);
if (left || right) {
value += right ? -10 : 10;
}
return `${value}px`;
},
calculatedTop() {
const originalValue = VTooltip.options.computed.calculatedTop.call(this);
if (!this.interactive) return originalValue;
const { top, bottom } = this;
let value = parseInt(originalValue);
if (top || bottom) {
value += bottom ? -10 : 10;
}
return `${value}px`;
},
styles() {
const originalValue = VTooltip.options.computed.styles.call(this);
if (!this.interactive) return originalValue;
const {
top, bottom, left, right,
} = this;
let paddingDirection;
if (bottom) paddingDirection = 'top';
else if (top) paddingDirection = 'bottom';
else if (right) paddingDirection = 'left';
else if (left) paddingDirection = 'right';
return {
...originalValue,
[`padding-${paddingDirection}`]: `${10}px`,
};
},
},
methods: {
onTooltipMouseenter(e) {
if (this.interactive) {
this.clearDelay();
this.isActive = true;
}
this.$emit('tooltip:mouseenter', e);
},
onTooltipMouseleave(e) {
if (this.interactive) {
this.clearDelay();
this.runDelay('close');
}
this.$emit('tooltip:mouseleave', e);
},
genContent() {
const content = this.$createElement('div', this.setBackgroundColor(this.color, {
style: this.contentStyles,
staticClass: 'v-tooltip__content',
class: {
[this.contentClass]: true,
menuable__content__active: this.isActive,
},
}), this.getContentSlot());
return this.$createElement('div', {
style: this.styles,
attrs: this.getScopeIdAttrs(),
class: {
'v-tooltip__wrapper': true,
'v-tooltip__wrapper--fixed': this.activatorFixed,
},
directives: [{
name: 'show',
value: this.isContentActive,
}],
on: {
mouseenter: this.onTooltipMouseenter,
mouseleave: this.onTooltipMouseleave,
},
ref: 'content',
}, [content]);
},
genActivatorListeners() {
const listeners = VTooltip.options.methods.genActivatorListeners.call(this);
if (this.interactive) {
if (listeners.mouseenter) {
listeners.mouseenter = (e) => {
this.getActivator(e);
this.clearDelay();
if (!this.isActive) {
this.runDelay('open');
}
};
}
}
return listeners;
},
},
};
</script>
<style lang="scss">
.v-tooltip__wrapper {
position: absolute;
&--fixed {
position: fixed;
}
.v-tooltip__content {
position: static;
}
}
</style>
I have some JSON data in dummyData. I am not sure how can I place the chat bubbles on left and right according to the direction. I am using Material UI and context API. Image for the reference. I don't want to use any library other than material UI.
Currently, every chat bubble is positioned to the left. How to position bubbles according to the direction. Code so far (CodeSandbox):
import React from 'react';
import makeStyles from '#material-ui/core/styles/makeStyles';
const useStyles = makeStyles(theme => ({
container: {
bottom: 0,
position: 'fixed'
},
bubbleContainer: {
width: '100%'
},
bubble: {
border: '0.5px solid black',
borderRadius: '10px',
margin: '5px',
padding: '10px',
display: 'inline-block'
}
}));
const ChatLayout = () => {
const classes = useStyles();
const dummyData = [
{
message: '1: This should be in left',
direction: 'left'
},
{
message: '2: This should be in right',
direction: 'right'
},
{
message: '3: This should be in left again',
direction: 'left'
}
];
const chatBubbles = dummyData.map((obj, i = 0) => (
<div className={classes.bubbleContainer}>
<div key={i++} className={classes.bubble}>
<div className={classes.button}>{obj.message}</div>
</div>
</div>
));
return <div className={classes.container}>{chatBubbles}</div>;
};
export default ChatLayout;
You can create separate div of chat bubble and apply CSS. And where you are receiving messages append the bubble div to your user list.