I've noticed some strange behavior of GSAP scrolling in GatsbyJS. I have a container with full-page sections that are scrolled with GSAP.
I open a tab in a browser and go to my website. The scrolling works fine. Then I open a new tab and go to (let's say) google.com and spend some time on that website. The tab with my website is still open in a background. Then I switch from google tab back to my website tab. After doing that GSAP 'breaks' and scrolls two full-page sections one after another. Even if I scroll a mouse wheel a bit. And sometimes GSAP can't scroll the section at all. It looks like a full-page starts moving but suddenly the scrolling stops and GSAP places the current section to it's place.
I never go to any other website within that browser tab. Also I never go to any another route of my website. I just open an index page -> switch to another browser tab -> and switch back to my website tab. I have no idea how and why but exactly these actions cause the problem.
P.S. resizing the browser window helps but after some time the scrolling breaks again
Code:
export default function IndexPage() {
useEffect(() => {
gsap.registerPlugin(ScrollTrigger)
gsap.registerPlugin(ScrollToPlugin)
const sections = document.querySelectorAll("section")
const scrolling = {
enabled: true,
events: "scroll,wheel,touchmove,pointermove".split(","),
prevent: e => e.preventDefault(),
disable() {
if (scrolling.enabled) {
scrolling.enabled = false
window.addEventListener("scroll", gsap.ticker.tick, { passive: true })
scrolling.events.forEach((e, i) =>
(i ? document : window).addEventListener(e, scrolling.prevent, {
passive: false,
})
)
}
},
enable() {
if (!scrolling.enabled) {
scrolling.enabled = true
window.removeEventListener("scroll", gsap.ticker.tick)
scrolling.events.forEach((e, i) =>
(i ? document : window).removeEventListener(e, scrolling.prevent)
)
}
},
}
function goToSection(section, anim, i) {
console.log(scrolling.enabled)
if (scrolling.enabled) {
// skip if a scroll tween is in progress
scrolling.disable()
gsap.to(window, {
scrollTo: { y: section, autoKill: false },
onComplete: scrolling.enable,
duration: 1,
})
anim && anim.restart()
}
console.log(section)
}
sections.forEach((section, i) => {
const intoAnim = gsap.from(section.querySelector(".right-col"), {
yPercent: 50,
duration: 1,
paused: true,
})
ScrollTrigger.create({
trigger: section,
start: "top bottom-=1",
end: "bottom top+=1",
onEnter: () => goToSection(section, intoAnim),
onEnterBack: () => goToSection(section),
})
})
return () => {
ScrollTrigger.kill()
}
}, [])
return (
<div className={styles.container}>
<Header />
<Products />
<Contact />
<Footer />
</div>
)
}
Related
The issue is like this.
When reloaded, the first step and pop-up show in the upper left corner instead of the center, this is the first problem for me. I expected it to position center as the Intro.js official document says.
Next, when you press "Next," the pop-up bites into the upper left corner and we can't see it. This is the second problem.
Furthermore, when "Next" is pressed, the pop-up also bites into the upper left corner and we can't see it. This is the second problem as well.
But furthermore, when "Next" is pressed, the pop-up appears in its normal expected position.
When you press "Next" again, the pop-up will still appear in the normal expected position.
The code looks like this.
import dynamic from 'next/dynamic';
import { useState } from 'react';
// Intro.js, see the details here: https://introjs.com/
// Intro.js-react, see the details here: https://github.com/HiDeoo/intro.js-react
// #ts-ignore
const Steps = dynamic(() => import('intro.js-react').then((mod) => mod.Steps), {
ssr: false
});
const Onboarding = () => {
const [stepEnabled, setStepEnabled] = useState(true);
const steps = [
{
title: 'Welcome!!',
intro:
'This is your dashboard. Once you have set up, numbers will be displayed.'
},
{
element: '#user-settings',
title: 'User Settings page',
intro: 'You can jump to the User Settings page from here.',
position: 'right'
},
{
element: '#profile-list',
intro: 'This is your profile list.',
position: 'right'
},
{
element: '#card-list',
intro: 'This is your card list.',
position: 'left'
}
];
const onExit = () => {
setStepEnabled(true);
};
const options = {
showProgress: true,
showBullets: true,
exitOnOverlayClick: true,
exitOnEsc: true,
nextLabel: 'Next',
prevLabel: 'Prev',
// skipLabel: 'Skip',
hidePrev: true,
doneLabel: 'Done',
overlayOpacity: 0.5,
overlayColor: '#000',
showStepNumbers: true,
keyboardNavigation: true,
scrollToElement: true,
helperElementPadding: 10,
showButtons: true
};
// if (!stepEnabled) {
// return null;
// }
return (
<Steps
// #ts-ignore
enabled={stepEnabled}
steps={steps}
initialStep={0}
onExit={onExit}
options={options}
/>
);
};
export default Onboarding;
Does anyone know why and how to fix it?
you can give customized css class to every object in intro.js like this :
()
{
element: ".test",
intro: "",
tooltipClass: "cssClassName1",
tooltipPosition: "bottom-center",
},
I'm trying to have a swipe animation when user swipes left in the device.
Here's a gist of the problem
When I click on about page button in Home page, the app smoothly navigates to About page.
When I hit on back icon button in the top bar of About page, it gives a back swipe animation.
I've implemented Hammer.js swipe left gesture for going back but it doesn't show any animation as that of back button animation. How could I achieve that?
I created a working example using StackBlitz to better explain the problem. Could anyone please help?
HTML
<ion-content (swipeleft)="goBack()">
<h2>About page</h2>
</ion-content>
TS
export class AboutPage implements OnInit {
constructor(private location: Location) {}
ngOnInit() {}
goBack() {
this.location.back();
}
}
Finally, I got this implemented using Ionic Gestures.
Here's the working example. Swipe left to right from about page, it will result a smooth animation and takes back to the page from you where came from.
Swipe Gesture Animation
async ngAfterViewInit() {
let gesture = await this.gestureCtrl.create({
el: this.aboutPage.nativeElement,
gestureName: 'swipe-left',
gesturePriority: 100,
threshold: 5,
passive: false,
onStart: () => {
this.renderer.setStyle(
this.aboutPage.nativeElement,
'transition',
'none'
);
},
onMove: (ev) => {
if (ev.deltaX > 0) {
this.renderer.setStyle(
this.aboutPage.nativeElement,
'transform',
`translateX(${ev.deltaX}px)`
);
}
},
onEnd: (ev) => {
this.renderer.setStyle(
this.aboutPage.nativeElement,
'transition',
'0.4s ease-out'
);
if (ev.deltaX > 100) {
this.renderer.setStyle(
this.aboutPage.nativeElement,
'transform',
'translateX(400px)'
);
this.location.back();
}
if (ev.deltaX < 100) {
this.renderer.setStyle(
this.aboutPage.nativeElement,
'transform',
'translateX(0px)'
);
}
},
});
gesture.enable(true);
}
Result of the animation
I'm trying to hide a column in ag-grid on mobile.
I'm using ag-grid for a vue.js app. I have tired the below code, but it's not working properly.
{
headerName: "Action",
field: "action",
minWidth: 54,
suppressMovable: true,
editable: true,
hide : ( window.innerWidth < 786 ? true : false )
}
I expect the output to be hide this column on mobile and show on desktop, but the output is bit strange to me. Initially when I load the page on mobile and desktop the column hide/show accordingly, but on mobile it's also hide some of other column's header titles(only header titles). Also when I resize the window from mobile to desktop the require column won't show and also resizing from desktop to mobile won't hide the required column.
You should use columnApi.setColumnVisible("your_column", false).
To be able to do so, first save the columnApi on #grid-ready
<template>
<ag-grid-vue style="width: 500px; height: 500px;"
class="ag-theme-balham"
:columnDefs="columnDefs"
:rowData="rowData"
rowSelection="multiple"
#grid-ready="onGridReady">
</template>
...
import {AgGridVue} from "ag-grid-vue";
const hideActionColumn = () => {
if(this.columnApi) {
this.columnApi.setColumnVisible("action", true);
if(window.innerWidth < 786) {
this.columnApi.setColumnVisible("action", false);
}
}
}
export default {
name: 'App',
data() {
return {
columnDefs: null,
rowData: null
}
},
components: {
AgGridVue
},
methods: {
onGridReady(params) {
this.columnApi = params.columnApi;
}
},
mounted() {
// hide the column, if the initial load is in mobile view
hideActionColumn();
window.addEventListener('resize', hideActionColumn);
},
beforeDestroy() {
window.removeEventListener('resize', hideActionColumn);
}
}
Then you need to appends an event listener, so you can call hideActionColumn() on window.resize
window.addEventListener('resize', hideActionColumn);
Also you need to remove the event listener, before your component is destroyed.
I hope this will help you.
Regards
So far in the API (v3.9.2), I see a mention of TouchRippleProps for ButtonBase for https://material-ui.com/api/button-base/
My button looks like
<Button variant="text"
size={"large"}
fullWidth
className={classes.button}
>
{value}
</Button>
and my button style is .
button: {
backgroundColor: colors.surface,
borderRadius: 0, // to make buttons sharp edged
touchRipple: colors.primary
}
When I touch a button, I see a white background (see number 5) as
My question is that When I touch a button, how can I change that background from white to let's say blue and then let it fade away?
UPDATE .
I achieved reasonable behavior with the following changes to your numberPadStyle:
const numberPadStyle = theme => ({
button: {
backgroundColor: colors.surface,
borderRadius: 0, // to make buttons sharp edged
"&:hover": {
backgroundColor: colors.primary,
// Reset on touch devices, it doesn't add specificity
"#media (hover: none)": {
backgroundColor: colors.surface,
"&:active": {
backgroundColor: colors.primary
}
}
},
"&:active": {
backgroundColor: colors.primary
}
}
});
The issue with touch screens is that a touch triggers the "hover" effect and it doesn't go away till you touch somewhere else. "#media (hover: none)" targets the hover effect for touch devices (https://developer.mozilla.org/en-US/docs/Web/CSS/#media/hover). The "active" CSS is in effect during the touch/click and then the ripple built in to Button takes care of the rest.
You can, of course, adjust the hover and active colors as desired.
It doesn't appear animations other than the ripple are supported. However, you can create something like this TriggeredAnimation wrapper component:
class TriggeredAnimationWrapper extends Component {
constructor(...args) {
super(...args)
this.state = {
wasClicked: false
}
this.triggerAnimation = this.triggerAnimation.bind(this)
}
triggerAnimation(callback) {
return (...args) => {
this.setState(
{wasClicked: true},
() => requestAnimationFrame(()=>this.setState({wasClicked: false}))
)
if (callback) return callback(...args)
}
}
render() {
const {
triggerAnimation,
props: {
children
},
state: {
wasClicked
}
} = this.props
return children({wasClicked, triggerAnimation})
}
}
And use it like this:
<TriggeredAnimationWrapper>({wasClicked, triggerAnimation}) => (
<Button
onClick={triggerAnimation((e) => console.log('clicked', e))}
className={wasClicked ? 'clicked' : ''}
/>
)</TriggeredAnimationWrapper>
Then, you can create a css animation and change the background when the click class is present.
In the first render the object comes like this
{
behindPosition: null,
chipBet: undefined,
chipSelectedId: null,
mainPosition: 4,
sitBehind: (),
submitbetBehind: bound componentDidUpdate(),
timer: false
}
as you see almost every prop is null or undefined
now there is a button in the view
<button id={`playerBehind${position}`}
onClick={this._enclosingFunctions}>Bet Behind</button>
once you click on that button, the object changes by adding more props, so:
{
behindPosition: 4,
mainPosition: 1,
chipSelectedId: null,
timer: true,
nickname: "omega",
refPosition: 4,
balance: 10000,
action: "",
onGame: true,
betAmount: 0,
simpleBetAmount: 0,
message: "",
lastBet: 0,
buttons: [],
position: 1
}
I recorded a video because we are using socket.io so you the error will be reproduced in the right browser within the video.
As you see there is a Bet Behind button in both views, once you click in the button on the left screen, the same button its been rendered in every spot of the right screen, which shouldn't be happening. I need that button to keep displayed only the spot where you saw it at the beginning of the video. The prop behindPosition is the position that you take once you click on the button.
So, all I need is to see that button in the spots where mainPosition is the same as refPosition, which is not happening now, the app is not taking into account anything of that, it is just rendering the buttons in every single spot. By spots I mean the white circles in the view which are 7. So there could be 7 mainPosition, and the button, again, should be displayed only when mainPosition is the matches refPosition
Now, here the way I have my code:
class BetBehindSpot extends Component {
static propTypes = {
'sitBehind' : React.PropTypes.func,
'behindPosition' : React.PropTypes.number,
'mainPosition' : React.PropTypes.number,// position of the spot owner (main player)
'refPosition' : React.PropTypes.number,
}
constructor (props) {
super(props);
}
render () {
console.log('on render', this.props);
const position = this.props.mainPosition;
const behindPosition = this.props.behindPosition;
let displayButton;
if (behindPosition) {
document.getElementById(`betbehind-spot${behindPosition}`).style.display = 'block';
} else if (behindPosition !== position) {
displayButton = (
<button id={`playerBehind${position}`}
className="betbehind-button" onClick={this._enclosingFunctions}>
<span>Bet Behind</span>
</button>
);
} else {
displayButton = null;
}
return (
<div>
{displayButton}
</div>
);
}
_enclosingFunctions = () => {
this._sitBehind();
}
_sitBehind = () => {
if (!this.props.refPosition) {
this.props.sitBehind();
}
}
}
so, what are your recommendations ?