74 lines
2.4 KiB
Python
74 lines
2.4 KiB
Python
from moviepy import Clip, ColorClip, CompositeVideoClip, vfx
|
|
|
|
|
|
# FadeIn
|
|
def fadein_transition(clip: Clip, t: float) -> Clip:
|
|
return clip.with_effects([vfx.FadeIn(t)])
|
|
|
|
|
|
# FadeOut
|
|
def fadeout_transition(clip: Clip, t: float) -> Clip:
|
|
return clip.with_effects([vfx.FadeOut(t)])
|
|
|
|
|
|
# SlideIn
|
|
def slidein_transition(clip: Clip, t: float, side: str) -> Clip:
|
|
width, height = clip.size
|
|
|
|
# MoviePy 内置 SlideIn 在当前这条处理链里对全屏素材不稳定,
|
|
# 会出现“逻辑上应用了转场,但画面几乎看不出变化”的情况。
|
|
# 这里改成显式黑底 + 位移动画,保证转场效果可见且行为可控。
|
|
def position(current_time: float):
|
|
progress = min(max(current_time / max(t, 0.001), 0), 1)
|
|
|
|
if side == "left":
|
|
return (-width + width * progress, 0)
|
|
if side == "right":
|
|
return (width - width * progress, 0)
|
|
if side == "top":
|
|
return (0, -height + height * progress)
|
|
if side == "bottom":
|
|
return (0, height - height * progress)
|
|
return (0, 0)
|
|
|
|
background = ColorClip(size=(width, height), color=(0, 0, 0)).with_duration(
|
|
clip.duration
|
|
)
|
|
moving_clip = clip.with_position(position)
|
|
return CompositeVideoClip([background, moving_clip], size=(width, height)).with_duration(
|
|
clip.duration
|
|
)
|
|
|
|
|
|
# SlideOut
|
|
def slideout_transition(clip: Clip, t: float, side: str) -> Clip:
|
|
width, height = clip.size
|
|
transition_start = max(clip.duration - t, 0)
|
|
|
|
# SlideOut 同样改成显式位移,保证片段末尾能稳定滑出画面。
|
|
def position(current_time: float):
|
|
if current_time <= transition_start:
|
|
return (0, 0)
|
|
|
|
progress = min(
|
|
max((current_time - transition_start) / max(t, 0.001), 0), 1
|
|
)
|
|
|
|
if side == "left":
|
|
return (-width * progress, 0)
|
|
if side == "right":
|
|
return (width * progress, 0)
|
|
if side == "top":
|
|
return (0, -height * progress)
|
|
if side == "bottom":
|
|
return (0, height * progress)
|
|
return (0, 0)
|
|
|
|
background = ColorClip(size=(width, height), color=(0, 0, 0)).with_duration(
|
|
clip.duration
|
|
)
|
|
moving_clip = clip.with_position(position)
|
|
return CompositeVideoClip([background, moving_clip], size=(width, height)).with_duration(
|
|
clip.duration
|
|
)
|