このコミットが含まれているのは:
@@ -2753,26 +2753,13 @@ const GekanatorBackdrop: FC<{
|
||||
nextThumbnails)
|
||||
const [flipVisualSeed, setFlipVisualSeed] = useState (visualSeed)
|
||||
const [isFlippingTiles, setIsFlippingTiles] = useState (false)
|
||||
const [isCrossfading, setIsCrossfading] = useState (false)
|
||||
|
||||
const isLeavingGuessBackdrop = displayedBackdropMode === 'guess' && backdropMode !== 'guess'
|
||||
|
||||
const renderBackdropMode = isLeavingGuessBackdrop ? backdropMode : displayedBackdropMode
|
||||
|
||||
const renderWinningRunCount = (isLeavingGuessBackdrop
|
||||
? winningRunQuestionCount
|
||||
: displayedWinningRunCount)
|
||||
|
||||
const renderThumbnails = isLeavingGuessBackdrop ? nextThumbnails : displayedThumbnails
|
||||
|
||||
const renderIsCrossfading = isCrossfading && !(isLeavingGuessBackdrop)
|
||||
|
||||
const renderedSettings = settingsForMode (renderBackdropMode)
|
||||
const renderedSettings = settingsForMode (displayedBackdropMode)
|
||||
const renderedTileCount = renderedSettings.columns * renderedSettings.rows
|
||||
const renderedScale = scaleForMode (renderBackdropMode, renderWinningRunCount)
|
||||
const renderedScale = scaleForMode (displayedBackdropMode, displayedWinningRunCount)
|
||||
|
||||
const isGuessPresentation = backdropMode === 'guess'
|
||||
const crossfadeDuration = motionMode === 'calm' ? .95 : .75
|
||||
const isGuessPresentation =
|
||||
backdropMode === 'guess' || displayedBackdropMode === 'guess'
|
||||
|
||||
useEffect (() => {
|
||||
guessAnimationControlsRef.current.forEach (control => control.stop ())
|
||||
@@ -2866,7 +2853,6 @@ const GekanatorBackdrop: FC<{
|
||||
{
|
||||
applyDirection ()
|
||||
setIsFlippingTiles (false)
|
||||
setIsCrossfading (false)
|
||||
setFlipVisualSeed (visualSeed)
|
||||
return
|
||||
}
|
||||
@@ -2874,7 +2860,6 @@ const GekanatorBackdrop: FC<{
|
||||
if (backdropMode === 'guess' && guessThumbnail)
|
||||
{
|
||||
setIsFlippingTiles (false)
|
||||
setIsCrossfading (false)
|
||||
setDisplayedBackdropMode ('guess')
|
||||
setDisplayedWinningRunCount (winningRunQuestionCount)
|
||||
setDisplayedThumbnails (nextThumbnails)
|
||||
@@ -2894,7 +2879,6 @@ const GekanatorBackdrop: FC<{
|
||||
setFromThumbnails (nextThumbnails)
|
||||
setToThumbnails (nextThumbnails)
|
||||
setIsFlippingTiles (false)
|
||||
setIsCrossfading (false)
|
||||
setFlipVisualSeed (visualSeed)
|
||||
return
|
||||
}
|
||||
@@ -2903,23 +2887,6 @@ const GekanatorBackdrop: FC<{
|
||||
{
|
||||
applyDirection ()
|
||||
setIsFlippingTiles (false)
|
||||
setIsCrossfading (false)
|
||||
setFlipVisualSeed (visualSeed)
|
||||
return
|
||||
}
|
||||
|
||||
if (displayedBackdropMode === 'guess' && backdropMode !== 'guess')
|
||||
{
|
||||
applyDirection ()
|
||||
x.set (0)
|
||||
y.set (0)
|
||||
setIsFlippingTiles (false)
|
||||
setIsCrossfading (false)
|
||||
setDisplayedBackdropMode (backdropMode)
|
||||
setDisplayedWinningRunCount (winningRunQuestionCount)
|
||||
setDisplayedThumbnails (nextThumbnails)
|
||||
setFromThumbnails (nextThumbnails)
|
||||
setToThumbnails (nextThumbnails)
|
||||
setFlipVisualSeed (visualSeed)
|
||||
return
|
||||
}
|
||||
@@ -2942,10 +2909,7 @@ const GekanatorBackdrop: FC<{
|
||||
|
||||
setFromThumbnails (currentThumbnails)
|
||||
setToThumbnails (nextThumbnails)
|
||||
const shouldCrossfade =
|
||||
displayedBackdropMode === 'winning_run' && backdropMode === 'normal'
|
||||
setIsCrossfading (shouldCrossfade)
|
||||
setIsFlippingTiles (!(shouldCrossfade))
|
||||
setIsFlippingTiles (true)
|
||||
|
||||
flipTimerRef.current = window.setTimeout (() => {
|
||||
setDisplayedBackdropMode (backdropMode)
|
||||
@@ -2954,11 +2918,10 @@ const GekanatorBackdrop: FC<{
|
||||
setFromThumbnails (nextThumbnails)
|
||||
setToThumbnails (nextThumbnails)
|
||||
setIsFlippingTiles (false)
|
||||
setIsCrossfading (false)
|
||||
applyDirection ()
|
||||
setFlipVisualSeed (visualSeed)
|
||||
flipTimerRef.current = null
|
||||
}, (shouldCrossfade ? crossfadeDuration : tileFlipDuration) * 1000)
|
||||
}, tileFlipDuration * 1000)
|
||||
|
||||
return () => {
|
||||
if (flipTimerRef.current !== null)
|
||||
@@ -2978,126 +2941,10 @@ const GekanatorBackdrop: FC<{
|
||||
visualSeed,
|
||||
activeDirection,
|
||||
winningRunQuestionCount,
|
||||
crossfadeDuration,
|
||||
tileFlipDuration,
|
||||
x,
|
||||
y])
|
||||
|
||||
const renderTileSet = (
|
||||
{ mode,
|
||||
thumbnails,
|
||||
settings,
|
||||
tileCount,
|
||||
scale,
|
||||
opacity,
|
||||
withFlip }: { mode: 'normal' | 'winning_run' | 'guess'
|
||||
thumbnails: string[]
|
||||
settings: { columns: number; rows: number; opacity: number }
|
||||
tileCount: number
|
||||
scale: number
|
||||
opacity?: number
|
||||
withFlip?: boolean }) => {
|
||||
const guessModeFlg = mode === 'guess'
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className="absolute inset-0"
|
||||
initial={guessModeFlg
|
||||
? false
|
||||
: (opacity == null
|
||||
? undefined
|
||||
: { opacity: opacity === 0 ? 1 : 0, scale, x: '0%', y: '0%' })}
|
||||
animate={{ opacity: opacity ?? 1,
|
||||
scale,
|
||||
x: guessModeFlg ? guessFocusOffset.x : '0%',
|
||||
y: guessModeFlg ? guessFocusOffset.y : '0%' }}
|
||||
transition={guessModeFlg
|
||||
? { duration: 0 }
|
||||
: (mode === 'winning_run'
|
||||
? { duration: crossfadeDuration, ease: [.16, 1, .3, 1] }
|
||||
: { duration: .2 })}>
|
||||
{Array.from ({ length: 9 }, (_, duplicate) => {
|
||||
const column = duplicate % 3
|
||||
const row = Math.floor (duplicate / 3)
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
key={`${ mode }:${ duplicate }`}
|
||||
className="absolute grid overflow-hidden"
|
||||
layout={mode === 'winning_run'}
|
||||
style={{
|
||||
left: `${ column * 33.333333 }%`,
|
||||
top: `${ row * 33.333333 }%`,
|
||||
width: '33.333333%',
|
||||
height: '33.333333%',
|
||||
gridTemplateColumns:
|
||||
`repeat(${ settings.columns }, minmax(0, 1fr))`,
|
||||
gridTemplateRows:
|
||||
`repeat(${ settings.rows }, minmax(0, 1fr))` }}
|
||||
transition={{ duration: tileFlipDuration, ease: 'easeInOut' }}>
|
||||
{Array.from ({ length: tileCount }, (_, index) => {
|
||||
const currentThumbnail =
|
||||
thumbnails[index % Math.max (thumbnails.length, 1)]
|
||||
const frontThumbnail =
|
||||
withFlip
|
||||
? fromThumbnails[index % Math.max (fromThumbnails.length, 1)]
|
||||
: currentThumbnail
|
||||
const backThumbnail =
|
||||
withFlip
|
||||
? toThumbnails[index % Math.max (toThumbnails.length, 1)]
|
||||
: currentThumbnail
|
||||
const thumbnail = currentThumbnail
|
||||
if (!(thumbnail) || !(frontThumbnail) || !(backThumbnail))
|
||||
return null
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
key={`${ mode }:${ duplicate }:${ index }`}
|
||||
className="relative overflow-hidden"
|
||||
layout={mode === 'winning_run'}
|
||||
transition={{ duration: tileFlipDuration, ease: 'easeInOut' }}
|
||||
style={{ perspective: 1600 }}>
|
||||
{(mode !== 'normal' || !(withFlip))
|
||||
? (
|
||||
<img
|
||||
src={['intro', 'end'].includes (phase)
|
||||
? mascotAsset
|
||||
: thumbnail}
|
||||
alt=""
|
||||
className="absolute inset-0 h-full w-full object-cover"
|
||||
style={{ opacity: settings.opacity }}/>)
|
||||
: (
|
||||
<motion.div
|
||||
className="absolute inset-0"
|
||||
initial={{ rotateY: 0 }}
|
||||
animate={{ rotateY: 180 }}
|
||||
transition={{
|
||||
duration: tileFlipDuration,
|
||||
ease: 'easeInOut' }}
|
||||
style={{ transformStyle: 'preserve-3d' }}>
|
||||
<img
|
||||
src={backThumbnail}
|
||||
alt=""
|
||||
className="absolute inset-0 h-full w-full object-cover"
|
||||
style={{
|
||||
backfaceVisibility: 'hidden',
|
||||
opacity: settings.opacity,
|
||||
transform: 'rotateY(180deg)' }}/>
|
||||
<img
|
||||
src={frontThumbnail}
|
||||
alt=""
|
||||
className="absolute inset-0 h-full w-full object-cover"
|
||||
style={{
|
||||
backfaceVisibility: 'hidden',
|
||||
opacity: settings.opacity }}/>
|
||||
</motion.div>)}
|
||||
</motion.div>)
|
||||
})}
|
||||
</motion.div>)
|
||||
})}
|
||||
</motion.div>)
|
||||
}
|
||||
|
||||
if (motionMode === 'off' || nextThumbnails.length === 0)
|
||||
return (
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-yellow-50 via-white
|
||||
@@ -3113,34 +2960,99 @@ const GekanatorBackdrop: FC<{
|
||||
width: 'calc(max(100vw, 100vh) * 3)',
|
||||
height: 'calc(max(100vw, 100vh) * 3)' }}>
|
||||
<motion.div
|
||||
className="relative h-full w-full">
|
||||
{renderIsCrossfading
|
||||
className="relative h-full w-full"
|
||||
animate={{ scale: renderedScale,
|
||||
x: displayedBackdropMode === 'guess' ? guessFocusOffset.x : '0%',
|
||||
y: displayedBackdropMode === 'guess' ? guessFocusOffset.y : '0%' }}
|
||||
transition={(displayedBackdropMode === 'winning_run'
|
||||
|| displayedBackdropMode === 'guess')
|
||||
? { duration: motionMode === 'calm' ? .95 : .75,
|
||||
ease: [.16, 1, .3, 1] }
|
||||
: { duration: .2 }}>
|
||||
{Array.from ({ length: 9 }, (_, duplicate) => {
|
||||
const column = duplicate % 3
|
||||
const row = Math.floor (duplicate / 3)
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
key={duplicate}
|
||||
className="absolute grid overflow-hidden"
|
||||
layout={displayedBackdropMode !== 'normal'}
|
||||
style={{
|
||||
left: `${ column * 33.333333 }%`,
|
||||
top: `${ row * 33.333333 }%`,
|
||||
width: '33.333333%',
|
||||
height: '33.333333%',
|
||||
gridTemplateColumns:
|
||||
`repeat(${ renderedSettings.columns }, minmax(0, 1fr))`,
|
||||
gridTemplateRows:
|
||||
`repeat(${ renderedSettings.rows }, minmax(0, 1fr))` }}
|
||||
transition={{ duration: tileFlipDuration, ease: 'easeInOut' }}>
|
||||
{Array.from ({ length: renderedTileCount }, (_, index) => {
|
||||
const currentThumbnail =
|
||||
displayedThumbnails[
|
||||
index % Math.max (displayedThumbnails.length, 1)]
|
||||
const frontThumbnail =
|
||||
isFlippingTiles
|
||||
? fromThumbnails[index % Math.max (fromThumbnails.length, 1)]
|
||||
: currentThumbnail
|
||||
const backThumbnail =
|
||||
isFlippingTiles
|
||||
? toThumbnails[index % Math.max (toThumbnails.length, 1)]
|
||||
: currentThumbnail
|
||||
const thumbnail =
|
||||
displayedBackdropMode === 'winning_run'
|
||||
|| displayedBackdropMode === 'guess'
|
||||
? nextThumbnails[index % Math.max (nextThumbnails.length, 1)]
|
||||
: currentThumbnail
|
||||
if (!(thumbnail) || !(frontThumbnail) || !(backThumbnail))
|
||||
return null
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
key={`${ duplicate }:${ index }`}
|
||||
className="relative overflow-hidden"
|
||||
layout={displayedBackdropMode !== 'normal'}
|
||||
transition={{ duration: tileFlipDuration, ease: 'easeInOut' }}
|
||||
style={{ perspective: 1600 }}>
|
||||
{(displayedBackdropMode !== 'normal' || !(isFlippingTiles))
|
||||
? (
|
||||
<>
|
||||
{renderTileSet ({
|
||||
mode: displayedBackdropMode,
|
||||
thumbnails: fromThumbnails,
|
||||
settings: settingsForMode (displayedBackdropMode),
|
||||
tileCount: (settingsForMode (displayedBackdropMode).columns
|
||||
* settingsForMode (displayedBackdropMode).rows),
|
||||
scale: scaleForMode (displayedBackdropMode, displayedWinningRunCount),
|
||||
opacity: 0 })}
|
||||
{renderTileSet ({
|
||||
mode: backdropMode,
|
||||
thumbnails: toThumbnails,
|
||||
settings: targetSettings,
|
||||
tileCount: targetTileCount,
|
||||
scale: scaleForMode (backdropMode, winningRunQuestionCount),
|
||||
opacity: 1 })}
|
||||
</>)
|
||||
<img
|
||||
src={['intro', 'end'].includes (phase)
|
||||
? mascotAsset
|
||||
: thumbnail}
|
||||
alt=""
|
||||
className="absolute inset-0 h-full w-full object-cover"
|
||||
style={{ opacity: renderedSettings.opacity }}/>)
|
||||
: (
|
||||
renderTileSet ({
|
||||
mode: renderBackdropMode,
|
||||
thumbnails: renderThumbnails,
|
||||
settings: renderedSettings,
|
||||
tileCount: renderedTileCount,
|
||||
scale: renderedScale,
|
||||
withFlip: isFlippingTiles && !(isLeavingGuessBackdrop) }))}
|
||||
<motion.div
|
||||
className="absolute inset-0"
|
||||
initial={{ rotateY: 0 }}
|
||||
animate={{ rotateY: 180 }}
|
||||
transition={{
|
||||
duration: tileFlipDuration,
|
||||
ease: 'easeInOut' }}
|
||||
style={{ transformStyle: 'preserve-3d' }}>
|
||||
<img
|
||||
src={backThumbnail}
|
||||
alt=""
|
||||
className="absolute inset-0 h-full w-full object-cover"
|
||||
style={{
|
||||
backfaceVisibility: 'hidden',
|
||||
opacity: renderedSettings.opacity,
|
||||
transform: 'rotateY(180deg)' }}/>
|
||||
<img
|
||||
src={frontThumbnail}
|
||||
alt=""
|
||||
className="absolute inset-0 h-full w-full object-cover"
|
||||
style={{
|
||||
backfaceVisibility: 'hidden',
|
||||
opacity: renderedSettings.opacity }}/>
|
||||
</motion.div>)}
|
||||
</motion.div>)
|
||||
})}
|
||||
</motion.div>)
|
||||
})}
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
新しい課題から参照
ユーザをブロックする