tailwind css into variants - framer motion - css

How to use tailwind css classes into framer motion objects..
const variant = {
hidden: {
'w-0'
},
visible: {
width: 400,
transition: {
type: 'spring',
stiffness: 170,
damping:30,
}
}
}
return (
<div className='flex relative w-full h-full'>
<button className='absolute z-40 top-0 left-0 origin-left bg-white rounded-lg px-6 py-2'>Open</button>
<motion.div variants={variant} initial="hidden" animate="visible" className="flex absolute bg-gray-100 top-0 bottom-0 ">
some
</motion.div>
</div>
)
in the above example, i would like to use in "hidden" property any tailwind css class but no idea how to implement it. "w-0" should be my approach but like i show it in that code it doesnt works

Unfortunately I don't think that this is possible to achieve
The best way is to do what you want is
const variant = {
hidden: {
// Property width directly
width:0
},
visible: {
width: 400,
transition: {
type: 'spring',
stiffness: 170,
damping:30,
}
}
}

you'd be able to do that by using twin.macro
import tw from 'twin.macro'
const variant = {
hidden: {
tw`w-0`
},
visible: {
width: 400,
transition: {
type: 'spring',
stiffness: 170,
damping:30,
}
}
}
return (
<div className='flex relative w-full h-full'>
<button className='absolute z-40 top-0 left-0 origin-left bg-white rounded-lg px-6 py-2'>Open</button>
<motion.div variants={variant} initial="hidden" animate="visible" className="flex absolute bg-gray-100 top-0 bottom-0 ">
some
</motion.div>
</div>
)

Related

React-Slick - transform: translate3d - Not working on smaller resolutions

I am using next.js and fetching data via API to slides. React-slick works great until the first break point on lower resolution (890px in this example).
.slick-track class has a property that is causing this problem.
.slick-track {transform: translate3d((wrong calc here)px, 0px, 0px)}
So instead 0px on first value inside translate 3d I'm getting value that is width of element where the list of slides should be shown. And List is moved to right so it cant bee seen.
After click on next button, list is coming to its normal place, but skipping first element in list...
Screenshot:
I have tried to overwrite this property inside module.css via :global and inside base CSS file, but it is still applied. I i put !important at end of my property it will be applied but I wont be able to scroll/navigate through elements of slide (first element will be stuck to left).
I also have tried older versions of react-slick 26.0; 26.1; 27.0; 28.0... But problem is still here.
Does anyone has some idea for solution?
Here is my component:
const Accessories = () => {
const { query } = useRegions();
const { error, data } = useAccessoriesSubcategoriesQuery({
variables: { ...query },
});
if (error) {
console.error("Accessories section component error", error.message);
}
const subcategoryItems = data?.categories.edges[0].node.children.edges || [];
// Slick slider settings
const settings = {
dots: false,
infinite: false,
speed: 500,
slidesToShow: 4,
slidesToScroll: 1,
nextArrow: <AccessoriesNextBtn currentSlide={0} slideCount={0} />,
prevArrow: <AccessoriesPrevBtn currentSlide={0} slideCount={0} />,
responsive: [
{
breakpoint: 890,
settings: {
slidesToShow: 3,
},
},
{
breakpoint: 620,
settings: {
slidesToShow: 2,
},
},
{
breakpoint: 420,
settings: {
slidesToShow: 1,
},
},
],
};
return (
<div className={"flex items-center justify-center w-full h-full bg-white"}>
<Container className="flex-col relative overflow-hidden">
<div className="flex flex-col lg:flex-row w-full mb-[50px] gap-[30px] lg:gap-[20px] justify-between">
<H2Headline className="text-[26px] text-left ">
Pool equipment & water treatment
</H2Headline>
<Button
className={`${styles.accessoriesSeeAllBtn} mb-[50px] mr-[0px] ml-auto xs:mb-0 xs:ml-0 xs:mr-auto max-w-[112px] h-[45px] box-border text-solidGray font-semibold text-[16px] leading-[157%] bg-white border border-solidGray hover:bg-solidGray hover:text-white`}
>
{/* <LinkBuilder linkItem={data?.categories.edges[0].node}>See all</LinkBuilder> */}
See All
</Button>
</div>
<div className={`${styles.accessoriesSliderWrapper} md:min-w-[500px] w-full`}>
<style jsx global>{`
.slick-track {
display: block;
transform: translate3d(0px, 0px, 0px);
}
`}</style>
<Slider {...settings}>
{subcategoryItems.map((item) => (
<div
key={item?.node.id}
className="flex flex-col max-w-[366px] md:max-w-[263px] transform hover:-translate-y-3 duration-500 ease-in-out "
>
{item.node.backgroundImage ? (
<div className="flex justify-center items-center content-center h-[280px] bg-lightGray rounded-[12px] overflow-visible z-50">
<Image
src={item.node.backgroundImage?.url}
width={218}
height={218}
alt="Category image"
className="object-cover object-center w-auto h-auto"
placeholder="blur"
blurDataURL={item.node.backgroundImage?.url}
/>
</div>
) : null}
<h3 className="text-solidGray text-[16px] leading-[160%] font-medium mt-[12px] mb-[3px]">
{item.node.name}
</h3>
<ParagraphText className="text-[14px] text-gray opacity-100 mt-[3px] mb-[12px]">
{item.node.description}
</ParagraphText>
<Button className="mx-auto text-[14px] border border-lightGray bg-white w-full hover:bg-solidGray hover:border-solidGray hover:text-white">
<LinkBuilder linkItem={item}>See all</LinkBuilder>
</Button>
</div>
))}
</Slider>
</div>
</Container>
</div>
);
};

GSAP Scrolltrigger PIN not working properly in nextjs

From my code below, If I comment out the pin: true property, the code works normally but the container that wraps the sections I expect to scroll horizontally are not sticky to the top. If I uncomment the pin: true, all the container (trigger) will not be visible.
Any suggestions on how to resolve this issue will be greatly appreciated.
import React, { useEffect } from "react";
import OverlayMenu from "./OverlayMenu";
import { gsap } from "gsap";
import ScrollTrigger from "gsap/dist/ScrollTrigger";
function MainContent({ overlayRef }) {
gsap.registerPlugin(ScrollTrigger);
useEffect(() => {
// alert(document.querySelector(".main__content").offsetWidth)
const sections = gsap.utils.toArray(".section");
gsap.to(sections, {
xPercent: -100 * (sections.length - 1),
ease: "none",
scrollTrigger: {
trigger: ".main__content",
scrub: 1,
markers: true,
start: "top top",
// // snap: 1 / (sections.length - 1),
end: "+=" + document.querySelector(".main__content").offsetWidth,
pin: true,
},
});
}, []);
return (
<div className="main__content__wrapper w-[calc(100%_-_80px)] h-screen ml-20">
<div className="w-full relative h-screen">
<OverlayMenu overlayRef={overlayRef} />
{/* <div className="w-full h-screen bg-black"></div> */}
<div className="main__content w-[300%] bg-purple-700 h-screen flex flex-nowrap">
<div className="section w-full h-screen- bg-red-500">1</div>
<div className="section w-full h-screen- bg-blue-500">2</div>
<div className="section w-full h-screen- bg-yellow-500">3</div>
</div>
</div>
</div>
);
}
export default MainContent;
Step back the version to 3.8.0
yarn add gsap#3.8.0
OR
npm install gsap#3.8.0
You can read more here:
https://greensock.com/forums/topic/30747-scrolltrigger-in-nextjs-build-not-working-reliably/#comment-153675
import React, { useEffect, useState } from "react";
import OverlayMenu from "./OverlayMenu";
import { gsap } from "gsap";
import ScrollTrigger from "gsap/dist/ScrollTrigger";
function MainContent({ overlayRef }) {
gsap.registerPlugin(ScrollTrigger);
const [hasRendered, setHasRendered] = useState(false);
useEffect(() => {
// ******This will set the state of hasRendered when the page render so as to keep track of the rendering steps *******
setHasRendered(true);
}, []);
useEffect(() => {
// ******* I added the conditional statement here to only create the instance of the gsap timeline ONLY after the page has rendered*******
if (hasRendered) {
// alert(document.querySelector(".main__content").offsetWidth)
const sections = gsap.utils.toArray(".section");
gsap.to(sections, {
xPercent: -100 * (sections.length - 1),
ease: "none",
scrollTrigger: {
trigger: ".main__content",
scrub: 1,
markers: true,
start: "top top",
// // snap: 1 / (sections.length - 1),
end: "+=" + document.querySelector(".main__content").offsetWidth,
pin: true,
},
});
}
}, [hasRendered]); //******** Dont forget the dependencies array too */
return (
<div className="main__content__wrapper w-[calc(100%_-_80px)] h-screen ml-20">
<div className="w-full relative h-screen">
<OverlayMenu overlayRef={overlayRef} />
{/* <div className="w-full h-screen bg-black"></div> */}
<div className="main__content w-[300%] bg-purple-700 h-screen flex flex-nowrap">
<div className="section w-full h-screen- bg-red-500">1</div>
<div className="section w-full h-screen- bg-blue-500">2</div>
<div className="section w-full h-screen- bg-yellow-500">3</div>
</div>
</div>
</div>
);
}
export default MainContent;

How to optimize drag animation (useState + transform: translate)

i have a Card component, which is draggable on mobile devices. on touch start it saves the initial point, on touch move it updates the current point state and on touch end it restores points and makes some logic with the swipe direction. it works fine when is testing on desktop but on mobile it is very sharp. how can i optimize drag animation so it works smooth?
i use "dragging" key to smooth relocate card on initial position if the distance is small
const Card = ({screens, title, overview, release_date, onSwipe}: props) => {
const [initialPoint, setInitialPoint] = useState({x: 0, y: 0})
const [currentPoint, setCurrentPoint] = useState({x: 0, y: 0})
const [dragging, setDragging] = useState(false)
const [currentStep, setCurrentStep] = useState(0)
const touchStartHandler = (e: React.TouchEvent) => {
setDragging(true)
setInitialPoint({
x: e.touches[0].pageX,
y: e.touches[0].pageY
})
setCurrentPoint({
x: e.touches[0].clientX,
y: e.touches[0].clientY
})
}
const touchMoveHandler = (e: React.TouchEvent) => {
setCurrentPoint({
x: e.touches[0].clientX,
y: e.touches[0].clientY
})
}
const touchEndHandler = () => {
if(currentPoint.x - initialPoint.x === 0) {
if(currentPoint.x > window.screen.width / 2) {if(currentStep < screens.length - 1) setCurrentStep(x => x + 1)}
else if(currentStep > 0) setCurrentStep(x => x - 1)
}
setDragging(false)
setCurrentPoint({x: 0, y: 0})
}
useEffect(() => {
preload(screens)
}, [screens])
return (
<div
style={{
transform: `translate(${currentPoint.x - initialPoint.x}px, ${currentPoint.y - initialPoint.y}px) rotate(${(window.screenX - currentPoint.x + initialPoint.x)/45}deg)`,
transition: dragging ? '0s' : '200ms'
}}
onTouchStart={touchStartHandler}
onTouchMove={touchMoveHandler}
onTouchEnd={touchEndHandler}
className={`w-[100%] h-[100%] box-border rounded-2xl absolute overflow-hidden bg-black`}
>
<div style={{backgroundImage: `url('${screens[currentStep]}')`}} className={'w-[120%] h-[120%] box-border rounded-2xl bg-black bg-cover bg-center blur-lg absolute left-[-10%] top-[-10%] opacity-50'}/>
<Stamps currentPoint={currentPoint} initialPoint={initialPoint}/>
<div className={'bg-contain bg-center bg-no-repeat h-full absolute w-full top-0 rounded-2xl'} style={{backgroundImage: `url('${screens[currentStep]}')`}}/>
<Counter current={currentStep} total={screens.length}/>
<div
className={'bg-rose-100 absolute bottom-0 rounded-2xl w-full text-black p-4'}>
<div className={'font-bold text-3xl'}>{title}</div>
<div className={'mt-2 font-medium'}>{release_date.split('-')[0]}, комедия, триллер</div>
<div className={'leading-4 mt-2 max-h-20 overflow-hidden relative'}>
{overview}
<div className={'bg-gradient-to-b from-transparent to-rose-100 absolute w-full h-full max-h-20 top-0'}/>
</div>
<div className={'text-lg font-medium underline'}>Полностью на themoviedb.org</div>
</div>
</div>
);
};
const Stamps = ({currentPoint, initialPoint}: {currentPoint: point, initialPoint: point}) => {
return (
<>
<div style={{opacity: (currentPoint.x - initialPoint.x - 50) / window.screen.width * 6}}
className={'font-bold text-green-400 text-4xl absolute top-28 z-30 border-4 border-green-400 p-4 rounded-2xl rotate-[35deg] left-12'}>НРАВИТСЯ</div>
<div style={{opacity: (-currentPoint.x + initialPoint.x - 50) / window.screen.width * 6}}
className={'font-bold text-red-400 text-4xl absolute top-28 z-30 border-4 border-red-400 p-4 rounded-2xl -rotate-[35deg] right-12'}>НУ ТАКОЕ</div>
<div style={Math.abs(currentPoint.x - initialPoint.x) < 50 ? {opacity: (-currentPoint.y + initialPoint.y - 50) / window.screen.height * 6} : {opacity: 0}}
className={'font-bold text-blue-400 text-4xl absolute bottom-56 z-30 border-4 border-blue-400 p-4 rounded-2xl -rotate-12 right-16'}>ПОСМОТРЮ</div>
<div style={Math.abs(currentPoint.x - initialPoint.x) < 50 ? {opacity: (currentPoint.y - initialPoint.y - 50) / window.screen.height * 6} : {opacity : 0}}
className={'font-bold text-gray-400 text-4xl absolute top-28 z-30 border-4 border-gray-400 p-4 rounded-2xl rotate-12 right-8'}>ПРОПУСТИТЬ</div>
</>
);
};
const Counter = ({current, total}: props) => {
const a = new Array(total).fill(false)
return (
<div className={'flex flex-row gap-4 h-5 pt-4 px-4 z-10 absolute top-0 w-full box-border'}>
{a.map((_, i) => {
if(i === current) return <div key={`active-${i}`} className={'bg-gradient-to-br from-[#FDABDD] to-[#374A5A] rounded-full flex h-full w-full'}/>
else return <div key={`passive-${i}`} className={'bg-rose-50 rounded-full flex h-full w-full'}/>
})}
</div>
);
};
also there is no more than 2 cards displayed in one time
lol, i just tried to add to Card class "will-change-transform" and it boosted my animation to 60 fps

Is there a way to transition a border in with Tailwind?

I'm trying to make it so that a border transitions on to the page smoothly once I reach a y point but I am having trouble with the transition animation. I'm using react and tailwind.
This is the code I have so far.
const Navbar = () => {
const [navStyles, setNavStyles] = useState(false);
useEffect(() => {
const handleNavStyles = () => {
if (window.scrollY > 80) {
setNavStyles(true);
} else {
setNavStyles(false);
}
};
window.addEventListener('scroll', handleNavStyles);
}, []);
return (
<header className="sticky top-0 z-10 backdrop-blur-md ">
<nav
className={`mx-auto flex max-w-screen-sm items-center space-x-3 py-3 px-4 sm:py-5 sm:px-0 ${
navStyles ? 'border-b transition duration-300 ease-in' : ''
}`}
>
<div>Navbar</div>
</nav>
</header>
);
};
Use transition-all instead of transition.

Display event inside a grid for a resource?

I am trying to make a scheduler with resources. The biggest challenge for me so far is to create the events as one piece (blue in the sample). Can please someone share what would be the best approach for this task?
Second question: Also I would like to scale this later, by changing the slot step (e.g. in the sample it is a step "by 1" so slots are 1,2,3,4,5.
But later, if I want to change the step e.g. "by 5" 1,5,10...
then let's say we have an even from [7,12] - it should then show from 5-15 etc.
new Vue({
el: "#vue",
data() {
return {
slot: null,
row: null,
events: [
{ row: 1, slots: [ 3, 4 ] },
{ row: 4, slots: [ 3, 9 ] },
],
}
},
methods: {
onSelectSlot (row, slot) {
this.row = row
this.slot = slot
},
}
});
.selected-slot {
#apply bg-purple-600;
}
.selected-row {
#apply bg-green-600;
}
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<link href="https://unpkg.com/tailwindcss#^1.0/dist/tailwind.min.css" rel="stylesheet">
<div id="vue" class="p-5 bg-red-800 h-screen">
<div class="flex flex-row w-full h-full overflow-x-scroll">
<div v-for="x in 10" :key="x" class="bg-white w-60">
<div class="bg-blue-400 min-w-full flex flex-col w-full sticky top-0">
<div class="bg-green-400 flex items-center justify-center min-w-full h-10 w-full sticky top-0 shadow z-20 border-2" :class="(row === x) ? 'selected-row' : ''">
Row {{ x }}
</div>
<div v-for="y in 50" :key="y" class="w-60 h-10 border bg-purple-400 cursor-pointer flex items-center px-2 hover:bg-purple-500" :class="(row === x && y === slot) ? 'selected-slot' : ''" #click="onSelectSlot(x, y)">
Slot {{ x + ',' + y }}
<!-- events -->
<div v-for="(event,index) in events" :key="index">
<div v-if="event.row === x && (event.slots[0] <= y && event.slots[1] >= y) " class="bg-blue-500">
Event
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Can please someone help me to display the blue events in this example as one piece?

Resources