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

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

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>
);
};

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.

tailwind css into variants - framer motion

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>
)

CSS : Text is overflowing from div

I'm using tailwind with react. The problem is that my paragraph text is quite long. I have applied overflow-hidden to the div. But it is still flowing outside div. But it is not visible. But it is ruining my UI. As the pic is also getting stretched out due to the text overflowing. How do I stop the text from overflowing. So I only get the text that is visible inside div. I am using subString(0,50). It will fix the issue, if I use a smaller number. But I want to know is there any other way to fix it. So that the text does not go outside the div and occupies full space of div only. I tried many other properties to fix this issue. But no success so far.
I have uploaded a gif of my problem on imgur
Code
import React, { useEffect, useState } from "react";
import { BsBoxArrowUpRight, BsPencilSquare, BsTrash } from "react-icons/bs";
import { getActors } from "../api/actor";
let currentPageNo = 0;
let limit = 20;
export default function Actors() {
const [actors, setActors] = useState([]);
const [reachedToEnd, setReachedToEnd] = useState(false);
const fetchActors = async (pageNo) => {
const { profiles, error } = await getActors(pageNo, limit);
if (!profiles.length) {
currentPageNo = pageNo - 1;
return setReachedToEnd(true);
}
setActors([...profiles]);
};
const handleOnNextClick = () => {
if (reachedToEnd) return;
currentPageNo += 1;
fetchActors(currentPageNo);
};
const handleOnPrevClick = () => {
if (currentPageNo <= 0) return;
currentPageNo -= 1;
fetchActors(currentPageNo);
};
useEffect(() => {
fetchActors(currentPageNo);
}, []);
return (
<div className="p-5">
<div className="grid grid-cols-4 gap-5">
{actors.map((actor) => {
return <ActorProfile profile={actor} key={actor.id} />;
})}
</div>
<div className="flex justify-end items-center space-x-3 mt-5">
<button
type="button"
className="text-primary dark:text-white hover:underline"
onClick={handleOnPrevClick}
>
Prev
</button>
<button
type="button"
className="text-primary dark:text-white hover:underline"
onClick={handleOnNextClick}
>
Next
</button>
</div>
</div>
);
}
const ActorProfile = ({ profile }) => {
if (!profile) return null;
const [showOptions, setShowOptions] = useState(false);
const handleOnMouseEnter = () => {
setShowOptions(true);
};
const handleOnMouseLeave = () => {
setShowOptions(false);
};
let acceptedNameLength = 15;
const getName = (name) => {
if (name.length <= acceptedNameLength) return name;
return name.substring(0, acceptedNameLength) + "...";
};
const { name, avatar, about = "" } = profile;
return (
<div className="bg-white dark:bg-secondary shadow dark:shadow rounded h-20 overflow-hidden">
<div
onMouseEnter={handleOnMouseEnter}
onMouseLeave={handleOnMouseLeave}
className="flex cursor-pointer relative"
>
<img
src={avatar}
alt={name}
className="w-20 aspect-square object-cover"
/>
<div className="px-2 flex-1">
<h1 className="text-xl text-primary dark:text-white whitespace-nowrap">
{getName(name)}
</h1>
<p className="text-primary dark:text-white opacity-75">
{about.substring(0, 30)}
</p>
</div>
<Options visible={showOptions} />
</div>
</div>
);
};
const Options = ({ visible, onEditClick, onDeleteClick }) => {
if (!visible) return null;
return (
<div className="absolute inset-0 bg-primary bg-opacity-25 backdrop-blur-sm flex justify-center items-center space-x-5">
{" "}
<button
onClick={onDeleteClick}
type="button"
className="text-primary bg-white p-2 rounded-full hover:opacity-80 transition"
>
<BsTrash />
</button>
<button
onClick={onEditClick}
type="button"
className="text-primary p-2 rounded-full bg-white hover:opacity-80 transition"
>
<BsPencilSquare />
</button>
</div>
);
};

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