admin管理员组文章数量:1434125
I'm implementing an expandable input field that transforms into a span when losing focus. The input expands on focus and shrinks when converting back to a span. While the width animation works correctly, I'm experiencing an unwanted flickering effect in the text content during the transition.
import React, { useState, useRef, useEffect } from 'react'
// Simple className merger function to replace cn()
const cn = (...classes: (string | undefined)[]) => {
return classes.filter(Boolean).join(' ')
}
interface EditableProps {
className?: string
inputClassName?: string
inlineTextClassName?: string
placeholder?: string
maxLength?: number
isBold?: boolean
onFocusChange?: (focused: boolean) => void
inlineText?: string
}
export default function Editable({
className,
inputClassName,
inlineTextClassName,
placeholder = 'Enter your text here...',
maxLength = 50,
isBold = false,
onFocusChange,
inlineText = 'inline text'
}: EditableProps) {
const [text, setText] = useState(placeholder)
const [isFocused, setIsFocused] = useState(false)
const inputRef = useRef<HTMLInputElement>(null)
const containerRef = useRef<HTMLDivElement>(null)
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (inputRef.current && !inputRef.current.contains(event.target as Node)) {
setIsFocused(false)
onFocusChange?.(false)
}
}
document.addEventListener('mousedown', handleClickOutside)
return () => document.removeEventListener('mousedown', handleClickOutside)
}, [onFocusChange])
const handleFocus = () => {
setIsFocused(true)
onFocusChange?.(true)
setTimeout(() => inputRef.current?.select(), 0)
}
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setText(e.target.value.slice(0, maxLength))
}
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') inputRef.current?.blur()
}
return (
<div className={cn('flex items-center w-full', className)}>
<div
ref={containerRef}
className={cn(
'transition-[flex] duration-300 ease-in-out',
isFocused ? 'flex-1' : 'flex-initial'
)}
>
{isFocused ? (
<div className="flex items-center bg-gray-100 rounded-md w-full">
<input
ref={inputRef}
value={text}
onChange={handleChange}
onBlur={() => setIsFocused(false)}
onKeyDown={handleKeyDown}
className={cn(
'input-ghost focus:outline-none px-2 py-1 border-none bg-gray-100 rounded-md w-full',
isBold ? 'font-bold' : 'font-normal',
inputClassName
)}
style={{ fontWeight: isBold ? 'bold' : 'normal' }}
/>
<div className="flex-shrink-0 px-2 text-sm text-gray-400">
{text.length}/{maxLength}
</div>
</div>
) : (
<span
onClick={handleFocus}
className={cn(
'cursor-pointer hover:opacity-80 px-2 py-1 inline-block',
isBold ? 'font-bold' : 'font-normal',
inputClassName
)}
>
{text}
</span>
)}
</div>
<span className={cn('ml-2 text-sm text-gray-500 flex-shrink-0', inlineTextClassName)}>{inlineText}</span>
</div>
)
}
Current Behavior:
- Input expands on focus
- Converts to span and shrinks on blur
- Text content flickers during the width animation
Expected Behavior:
- Smooth transition between input and span states
- No text flickering during the animation
How can I eliminate the text flickering effect while maintaining the smooth width animation during the input-to-span transition?
I'm implementing an expandable input field that transforms into a span when losing focus. The input expands on focus and shrinks when converting back to a span. While the width animation works correctly, I'm experiencing an unwanted flickering effect in the text content during the transition.
import React, { useState, useRef, useEffect } from 'react'
// Simple className merger function to replace cn()
const cn = (...classes: (string | undefined)[]) => {
return classes.filter(Boolean).join(' ')
}
interface EditableProps {
className?: string
inputClassName?: string
inlineTextClassName?: string
placeholder?: string
maxLength?: number
isBold?: boolean
onFocusChange?: (focused: boolean) => void
inlineText?: string
}
export default function Editable({
className,
inputClassName,
inlineTextClassName,
placeholder = 'Enter your text here...',
maxLength = 50,
isBold = false,
onFocusChange,
inlineText = 'inline text'
}: EditableProps) {
const [text, setText] = useState(placeholder)
const [isFocused, setIsFocused] = useState(false)
const inputRef = useRef<HTMLInputElement>(null)
const containerRef = useRef<HTMLDivElement>(null)
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (inputRef.current && !inputRef.current.contains(event.target as Node)) {
setIsFocused(false)
onFocusChange?.(false)
}
}
document.addEventListener('mousedown', handleClickOutside)
return () => document.removeEventListener('mousedown', handleClickOutside)
}, [onFocusChange])
const handleFocus = () => {
setIsFocused(true)
onFocusChange?.(true)
setTimeout(() => inputRef.current?.select(), 0)
}
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setText(e.target.value.slice(0, maxLength))
}
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') inputRef.current?.blur()
}
return (
<div className={cn('flex items-center w-full', className)}>
<div
ref={containerRef}
className={cn(
'transition-[flex] duration-300 ease-in-out',
isFocused ? 'flex-1' : 'flex-initial'
)}
>
{isFocused ? (
<div className="flex items-center bg-gray-100 rounded-md w-full">
<input
ref={inputRef}
value={text}
onChange={handleChange}
onBlur={() => setIsFocused(false)}
onKeyDown={handleKeyDown}
className={cn(
'input-ghost focus:outline-none px-2 py-1 border-none bg-gray-100 rounded-md w-full',
isBold ? 'font-bold' : 'font-normal',
inputClassName
)}
style={{ fontWeight: isBold ? 'bold' : 'normal' }}
/>
<div className="flex-shrink-0 px-2 text-sm text-gray-400">
{text.length}/{maxLength}
</div>
</div>
) : (
<span
onClick={handleFocus}
className={cn(
'cursor-pointer hover:opacity-80 px-2 py-1 inline-block',
isBold ? 'font-bold' : 'font-normal',
inputClassName
)}
>
{text}
</span>
)}
</div>
<span className={cn('ml-2 text-sm text-gray-500 flex-shrink-0', inlineTextClassName)}>{inlineText}</span>
</div>
)
}
Current Behavior:
- Input expands on focus
- Converts to span and shrinks on blur
- Text content flickers during the width animation
Expected Behavior:
- Smooth transition between input and span states
- No text flickering during the animation
How can I eliminate the text flickering effect while maintaining the smooth width animation during the input-to-span transition?
Share Improve this question edited Nov 18, 2024 at 18:45 j08691 208k32 gold badges269 silver badges280 bronze badges asked Nov 18, 2024 at 13:32 Yehezkiel LYehezkiel L 4394 silver badges13 bronze badges1 Answer
Reset to default 1It seems the cause of the flicker is due to the properties you are transitioning. Namely, flex-basis
, where you toggle between auto
(implicitly from flex-initial
) and 0%
(from flex-1
).
Instead, it sounds like you'd want to have the width stop/start at the width of the <span>
. If so, consider transitioning and changing flex-grow
only:
const { useState, useRef, useEffect } = React;
// Simple className merger function to replace cn()
const cn = (...classes) => {
return classes.filter(Boolean).join(' ')
}
function Editable({
className,
inputClassName,
inlineTextClassName,
placeholder = 'Enter your text here...',
maxLength = 50,
isBold = false,
onFocusChange,
inlineText = 'inline text'
}) {
const [text, setText] = useState(placeholder)
const [isFocused, setIsFocused] = useState(false)
const inputRef = useRef(null)
const containerRef = useRef(null)
useEffect(() => {
const handleClickOutside = (event) => {
if (inputRef.current && !inputRef.current.contains(event.target)) {
setIsFocused(false)
onFocusChange(false)
}
}
document.addEventListener('mousedown', handleClickOutside)
return () => document.removeEventListener('mousedown', handleClickOutside)
}, [onFocusChange])
const handleFocus = () => {
setIsFocused(true)
onFocusChange(true)
setTimeout(() => inputRef.current.select(), 0)
}
const handleChange = (e) => {
setText(e.target.value.slice(0, maxLength))
}
const handleKeyDown = (e) => {
if (e.key === 'Enter') inputRef.current.blur()
}
return (
<div className={cn('flex items-center w-full', className)}>
<div
ref={containerRef}
className={cn(
'transition-[flex-grow] duration-300 ease-in-out',
isFocused ? 'grow' : 'flex-initial'
)}
>
{isFocused ? (
<div className="flex items-center bg-gray-100 rounded-md w-full">
<input
ref={inputRef}
value={text}
onChange={handleChange}
onBlur={() => setIsFocused(false)}
onKeyDown={handleKeyDown}
className={cn(
'input-ghost focus:outline-none px-2 py-1 border-none bg-gray-100 rounded-md w-full',
isBold ? 'font-bold' : 'font-normal',
inputClassName
)}
style={{ fontWeight: isBold ? 'bold' : 'normal' }}
/>
<div className="flex-shrink-0 px-2 text-sm text-gray-400">
{text.length}/{maxLength}
</div>
</div>
) : (
<span
onClick={handleFocus}
className={cn(
'cursor-pointer hover:opacity-80 px-2 py-1 inline-block',
isBold ? 'font-bold' : 'font-normal',
inputClassName
)}
>
{text}
</span>
)}
</div>
<span className={cn('ml-2 text-sm text-gray-500 flex-shrink-0', inlineTextClassName)}>{inlineText}</span>
</div>
)
}
ReactDOM.createRoot(document.getElementById('app')).render(<Editable onFocusChange={() => {}} />);
<script src="https://cdnjs.cloudflare/ajax/libs/react/18.3.1/umd/react.production.min.js" integrity="sha512-QVs8Lo43F9lSuBykadDb0oSXDL/BbZ588urWVCRwSIoewQv/Ewg1f84mK3U790bZ0FfhFa1YSQUmIhG+pIRKeg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare/ajax/libs/react-dom/18.3.1/umd/react-dom.production.min.js" integrity="sha512-6a1107rTlA4gYpgHAqbwLAtxmWipBdJFcq8y5S/aTge3Bp+VAklABm2LO+Kg51vOWR9JMZq1Ovjl5tpluNpTeQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.tailwindcss/3.4.15"></script>
<div id="app"></div>
版权声明:本文标题:reactjs - How to prevent text flicker during input-to-span transition animation tailwind react - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745613847a2666276.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论