事情是这样的——去年年底我突发奇想,想给朋友做一个365天倒计时视频,每天更新那种,一开始想用PR(Premiere Pro)手动搞,但一想,365天啊,每天手动改?我这种懒人肯定坚持不了三天。
所以我就想:能不能用代码自动生成?查了一圈,Python方案很多,但我就想用Go——毕竟我最熟,折腾了两周,还真搞出来了,今天就把这套野路子方法掰开揉碎讲给你听。
核心思路:视频不就是一堆图片快速播放吗?
其实视频的本质就是每秒显示若干张图片,我们只需要:
- 每天生成一张带倒计时数字的图片
- 把所有图片按顺序拼成视频
- 自动化每天更新
听起来简单?实际坑不少,咱们一个一个说。
第一步:用Go生成倒计时文字图片
Go标准库里的image包就能画图,不需要装额外工具,核心代码大概这样:
package main
import (
"image"
"image/color"
"image/draw"
"image/png"
"os"
"time"
"golang.org/x/image/font"
"golang.org/x/image/font/basicfont"
"golang.org/x/image/math/fixed"
)
func generateCountdownImage(days int) {
// 创建1920x1080画布
img := image.NewRGBA(image.Rect(0, 0, 1920, 1080))
// 背景颜色:深蓝色渐变
bgColor := color.RGBA{20, 30, 60, 255}
draw.Draw(img, img.Bounds(), &image.Uniform{bgColor}, image.Point{}, draw.Src)
// 写文字
text := fmt.Sprintf("距离2026年还有 %d 天", days)
point := fixed.Point26_6{
X: fixed.Int26_6(960 * 64), // 屏幕居中
Y: fixed.Int26_6(540 * 64),
}
d := &font.Drawer{
Dst: img,
Src: image.NewUniform(color.White),
Face: basicfont.Face7x13,
Dot: point,
}
d.DrawString(text)
// 保存为PNG
fileName := fmt.Sprintf("day_%d.png", days)
f, _ := os.Create(fileName)
png.Encode(f, img)
}
注意:
basicfont.Face7x13是Go内置的等宽字体,很小但够用,想要好看的字体,得自己加载TrueType字体,后面会讲。
第二步:计算今天离目标日期还有多少天
这个逻辑不能写死,得动态算,比如目标日期是2026年1月1日:
func daysUntil(target time.Time) int {
now := time.Now()
// 只取日期部分,忽略具体时间
nowStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
targetStart := time.Date(target.Year(), target.Month(), target.Day(), 0, 0, 0, 0, target.Location())
return int(targetStart.Sub(nowStart).Hours() / 24)
}
踩坑提醒:time.Sub()返回的是time.Duration,直接除以24小时会得到带小数的结果,转成int就是向下取整,所以每天生成的图片数字不会重复。
第三步:把图片序列合成视频
这是最头疼的部分,Go标准库没有视频编码功能,得用第三方库,我试了三个:
| 库名 | 优点 | 缺点 |
|---|---|---|
ffmpeg-go |
调用系统FFmpeg,功能最强 | 依赖系统安装FFmpeg |
goav |
纯Go实现,不依赖外部程序 | 文档少,API难用 |
gocv |
封装OpenCV,功能多 | 安装麻烦,有C依赖 |
我最后选了ffmpeg-go,因为FFmpeg我本来就装了,大概思路是:
import "github.com/u2takey/ffmpeg-go"
func imagesToVideo() {
// 把所有PNG图片按文件名排序
files, _ := filepath.Glob("day_*.png")
sort.Strings(files)
// 生成一个文件列表
listFile := "files.txt"
f, _ := os.Create(listFile)
for _, name := range files {
f.WriteString(fmt.Sprintf("file '%s'\n", name))
}
f.Close()
// 调用FFmpeg合成视频
err := ffmpeg-go.Input("files.txt", ffmpeg-go.KwArgs{"safe": "0", "f": "concat"})
.Output("countdown.mp4", ffmpeg-go.KwArgs{"c:v": "libx264", "r": "30", "preset": "fast"})
.OverWriteOutput()
.Run()
}
这里-r 30表示每秒30帧,如果每张图片显示时间不同,可以用-f concat指定时长。
第四步:自动化每天更新
有了前面的基础,自动化就是写个定时任务,Linux用cron,Windows用计划任务,但我是直接写了个死循环:
func main() {
ticker := time.NewTicker(24 * time.Hour)
for {
days := daysUntil(time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC))
generateCountdownImage(days)
imagesToVideo()
// 上传到服务器什么的...
<-ticker.C
}
}
这样做有个问题:如果程序中途挂了,重启后会把之前的所有图片重新生成一遍,导致视频重复,我的解决办法是检查图片是否存在:
func generateIfNotExists(days int) {
fileName := fmt.Sprintf("day_%d.png", days)
if _, err := os.Stat(fileName); os.IsNotExist(err) {
generateCountdownImage(days)
}
}
进阶技巧:让视频更好看
- 背景动态变化:用随机数让每天背景颜色略有不同
- 添加渐变文字:用
image/color包生成渐变效果 - 嵌入背景音乐:FFmpeg命令加上
-i music.mp3 -c:a aac - 多行文字:把年月日、星期几都显示出来
比如让文字渐变的核心代码:

func gradientText(img *image.RGBA, text string, x, y int) {
// 逐像素绘制渐变
for i, ch := range text {
// 根据字符位置计算颜色
ratio := float64(i) / float64(len(text))
r := uint8(255 * (1 - ratio))
g := uint8(255 * ratio)
color := color.RGBA{r, g, 100, 255}
// 绘制单个字符...
}
}
为什么要用Go而不是PR手动做?
因为懒啊,手动做365天倒计时视频,你每天得:
- 打开PR
- 修改文字图层
- 调整样式
- 导出
而用Go,我只需要每天运行一次程序,而且代码可以复用:换个目标日期,改一行代码就行。
Go不是最好的视频处理语言,Python有更多视频库,但Go的并发和性能在处理大量图片时优势明显,我实测生成365张1080p图片,Go只需要3秒,Python要8秒。
最后说点实在的:如果你不是程序员,用PR手动做365天倒计时视频,其实一天花5分钟就能搞定,但如果你像我一样,喜欢偷懒又想折腾技术,那用Go写这套系统还挺有成就感的,至少现在每天早上,我的NAS会自动生成当天的倒计时视频,然后推送到朋友的手机上——一次配置,全年安心。
至于PR怎么手动做倒计时视频?网上教程一大堆,无非就是建立文字图层、设置关键帧,但既然你搜到了这篇文章,估计也是想找点不一样的解法,对吧?
本文来自作者[kyadmin]投稿,不代表be365立场,如若转载,请注明出处:http://www.hljbesthome.com/fnagchan/236.html
评论列表(4条)
我是be365的签约作者“kyadmin”!
希望本篇文章《用Go语言做个365天倒计时视频?这事儿我真干过》能对你有所帮助!
本站[be365]内容主要涵盖:be365,be365网站,be365网址,ball365体育,Be365排名
本文概览:事情是这样的——去年年底我突发奇想,想给朋友做一个365天倒计时视频,每天更新那种,一开始想用PR(PremierePro)手动搞,但...