这是测试站点!!!!

不要保存重要内容!不要保存重要内容!不要保存重要内容!不要保存重要内容!不要保存重要内容!

模块:抖动:修订间差异

来自Limbo Wiki
晨茗biu~​(留言 | 贡献)
文字抖动模板
标签:(旧)WikiEditor
 
晨茗biu~​(留言 | 贡献)
小无编辑摘要
标签:(旧)WikiEditor
 
第1行: 第1行:
-- 模块文档见 [[Module:抖动/doc]]
local p = {}
local p = {}


-- 模式 → 类前缀 + 档位参数名
-- 模式 → 类前缀 + 数值参数名 + 默认数值
local modeDef = {
local modeDef = {
['平移'] = { pre = 'translate', levelParam = '平移幅度' },
['平移'] = { pre = 'translate', numParam = '平移幅度', def = '0.11' },
['旋转'] = { pre = 'rotate',    levelParam = '旋转角度' },
['旋转'] = { pre = 'rotate',    numParam = '旋转角度', def = '9' },
['跳动'] = { pre = 'bounce',    levelParam = '跳动高度' },
['跳动'] = { pre = 'bounce',    numParam = '跳动高度', def = '0.17' },
['摇摆'] = { pre = 'swing',    levelParam = '摇摆角度' },
['摇摆'] = { pre = 'swing',    numParam = '摇摆角度', def = '9' },
['颤抖'] = { pre = 'jitter',    levelParam = '颤抖幅度' },
['颤抖'] = { pre = 'jitter',    numParam = '颤抖幅度', def = '0.09' },
['缩放'] = { pre = 'pulse',    levelParam = '缩放倍数' },
['缩放'] = { pre = 'pulse',    numParam = '缩放倍数', def = '1.1' },
}
}


-- 中文档位 → 数字档位
-- 颤抖角度参数(颤抖为位移 + 角度两个变量)
local cn = {
local JITTER_ANGLE = '颤抖角度'
['微小'] = 1, ['极微'] = 1, ['微'] = 1,
local JITTER_DEF_ANGLE = '6'
['小'] = 2,  ['较小'] = 2,
['中'] = 4,  ['中等'] = 4,
['较大'] = 6, ['大'] = 6,
['特大'] = 8, ['很大'] = 8,
['巨大'] = 10, ['极大'] = 10,
}


-- 解析档位:数字 1~10
-- 解析数值:非法/空 → 默认值
local function levelOf(v)
local function numOf(v, def)
if v == nil or v == '' then return 5 end
if v == nil then return def end
if cn[v] then return cn[v] end
v = tostring(v):match('^%s*(.-)%s*$')
if v == nil or v == '' then return def end
local n = tonumber(v)
local n = tonumber(v)
if not n then return 5 end
if not n then return def end
n = math.floor(n)
return tostring(n)
if n < 1 then return 1 end
if n > 10 then return 10 end
return n
end
end


-- 模式拆分
-- 模式拆分
local function splitModes(s)
local function splitModes(s)
if not s or s == '' then return { '颤抖' } end  
if not s or s == '' then return { '颤抖' } end
local out, seen = {}, {}
local out, seen = {}, {}
for m in mw.ustring.gmatch(s, '[^%+、,, ]+') do
for m in mw.ustring.gmatch(s, '[^%+、,, ]+') do
第43行: 第36行:
end
end
end
end
if #out == 0 then return { '颤抖' } end
if #out == 0 then return { '颤抖' } end
return out
return out
end
-- 生成该模式的行内 CSS 变量声明
local function varDecls(m, args)
local def = modeDef[m]
local parts = {}
if m == '颤抖' then
parts[#parts + 1] = '--dk-amp:' .. numOf(args[def.numParam], def.def) .. 'em'
parts[#parts + 1] = '--dk-angle:' .. numOf(args[JITTER_ANGLE], JITTER_DEF_ANGLE) .. 'deg'
elseif m == '跳动' then
parts[#parts + 1] = '--dk-amp:-' .. numOf(args[def.numParam], def.def) .. 'em'
elseif m == '缩放' then
parts[#parts + 1] = '--dk-scale:' .. numOf(args[def.numParam], def.def)
elseif m == '平移' then
parts[#parts + 1] = '--dk-amp:' .. numOf(args[def.numParam], def.def) .. 'em'
else -- 旋转 / 摇摆
parts[#parts + 1] = '--dk-angle:' .. numOf(args[def.numParam], def.def) .. 'deg'
end
return parts
end
end


第52行: 第64行:
for i = #modes, 1, -1 do
for i = #modes, 1, -1 do
local m = modes[i]
local m = modes[i]
local cls = 'dk-' .. modeDef[m].pre .. '-' .. info.levels[i]
local cls = 'dk-' .. modeDef[m].pre
local parts = {}
local parts = {}
for _, v in ipairs(info.vars[i]) do parts[#parts + 1] = v end
for _, pt in ipairs(info.styles[i]) do parts[#parts + 1] = pt end
for _, pt in ipairs(info.styles[i]) do parts[#parts + 1] = pt end
if perChar and i == 1 then
if perChar and i == 1 then
-- 逐字:最外层加 shake-char,延迟改用错位相位
local filtered = {}
local filtered = {}
for _, pt in ipairs(parts) do
for _, pt in ipairs(parts) do
第82行: 第94行:
local modes = splitModes(args['模式'])
local modes = splitModes(args['模式'])


local info = { levels = {}, styles = {} }
local info = { vars = {}, styles = {} }
for i, m in ipairs(modes) do
for i, m in ipairs(modes) do
local def = modeDef[m]
info.vars[i] = varDecls(m, args)
info.levels[i] = levelOf(args[def.levelParam])
local parts = {}
local parts = {}
local dur = args[m .. '速度']
local dur = args[m .. '速度']
第97行: 第108行:


if perChar then
if perChar then
-- 逐字模式:拆成单个字符(纯文本)
local out, i = {}, 0
local out, i = {}, 0
for ch in mw.ustring.gmatch(text, '.') do
for ch in mw.ustring.gmatch(text, '.') do
第109行: 第119行:
return '<span class="shake-chars">' .. table.concat(out) .. '</span>'
return '<span class="shake-chars">' .. table.concat(out) .. '</span>'
else
else
-- 整段模式:内容作为 wikitext 解析
return wrap(frame:preprocess(text), modes, info, false, 1)
return wrap(frame:preprocess(text), modes, info, false, 1)
end
end

2026年8月31日 (一) 21:13的最新版本

模板:抖动的底层生成器,负责把模板参数转换为最终的 HTML 结构。

工作原理

模块按以下步骤工作:

  1. 读取参数:从调用中取得「内容、模式、逐字、速度、延迟」以及各模式对应的数值幅度参数。
  2. 解析模式:把 模式=平移+旋转+跳动 这类字符串拆分成模式列表。
  3. 解析数值:把用户传入的纯数字幅度值规整为合法数值,空值或非数字时回退为程序内置默认值。
  4. 生成变量:把各模式的幅度数值转换为行内 CSS 自定义属性(CSS 变量),如 --dk-amp:0.05em。
  5. 生成结构:为每个模式各包一层 <span> 并依次嵌套;逐字模式下把文字按单个字符拆开。
  6. 输出 HTML:模块只生成 class 名、行内 CSS 变量和必要的内联样式,动画本身由模板:抖动/styles.css提供。

模式叠加的原理

CSS 中同一元素只能运行一个 transform 动画,无法直接叠加。因此模块让每种模式各占一层 <span>:

<span class="dk-translate" style="--dk-amp:0.11em">    ← 平移层
  <span class="dk-rotate" style="--dk-angle:9deg">     ← 旋转层
    <span class="dk-bounce" style="--dk-amp:-0.17em">  ← 跳动层
      文字
    </span>
  </span>
</span>

各层动画独立运行,视觉上自然叠加:外层负责整体平移,内层负责局部抖动。

逐字与整段

  • 整段模式:内容作为整体被包裹,先经 frame:preprocess() 处理,支持 wikitext 语法。
  • 逐字模式:用 mw.ustring.gmatch(text, '.') 将文字按字符拆开,每个字符成为独立抖动单元,并配以递增的负值动画延迟,使各字错位、不同步。逐字模式仅适用于纯文本。

与样式表的关系

模块不包含任何动画定义,只负责产出:

  • 形如 dk-模式 的 class(如 dk-translate);
  • 各模式幅度对应的行内 CSS 自定义属性(如 style="--dk-amp:0.05em");
  • 用户显式设置的速度 / 延迟内联样式(如 style="animation-duration:0.3s")。

实际动画(keyframes、位移、旋转角度等)全部定义在模板:抖动/styles.css,二者通过 class 名和 CSS 变量对应。模块输出的 class 与 CSS 变量对应关系:

模式 类名 行内 CSS 变量
平移 dk-translate --dk-amp: <幅度>em
旋转 dk-rotate --dk-angle: <角度>deg
跳动 dk-bounce --dk-amp: -<高度>em
摇摆 dk-swing --dk-angle: <角度>deg
颤抖 dk-jitter --dk-amp: <幅度>em; --dk-angle: <角度>deg
缩放 dk-pulse --dk-scale: <倍数>

参数与默认值

以下为模块读取的全部参数及其程序内置默认值。模式专属参数只在对应模式被启用时生效。

参数 作用模式 默认值 单位 说明
内容 全局 空(必填) — 要抖动的文字;也可作为第 1 个匿名参数
模式 全局 颤抖 — 抖动模式:平移/旋转/跳动/摇摆/颤抖/缩放,用 + 叠加
逐字 全局 否 — 设为 是 时逐字独立抖动
速度 全局 各模式自带周期
(平移 0.5s / 旋转 0.6s / 跳动 0.7s / 摇摆 0.8s / 颤抖 0.18s / 缩放 0.8s)
秒 全局动画周期,越小越快;被模式专属速度覆盖
延迟 全局 0 秒 全局动画延迟;被模式专属延迟覆盖
平移幅度 平移 0.11 em 水平位移幅度
平移速度 平移 继承全局 速度;否则 0.5 秒 平移层动画周期
平移延迟 平移 继承全局 延迟;否则 0 秒 平移层延迟
旋转角度 旋转 9 deg 旋转角度
旋转速度 旋转 继承全局 速度;否则 0.6 秒 旋转层动画周期
旋转延迟 旋转 继承全局 延迟;否则 0 秒 旋转层延迟
跳动高度 跳动 0.17 em 向上跳动高度
跳动速度 跳动 继承全局 速度;否则 0.7 秒 跳动层动画周期
跳动延迟 跳动 继承全局 延迟;否则 0 秒 跳动层延迟
摇摆角度 摇摆 9 deg 摇摆角度
摇摆速度 摇摆 继承全局 速度;否则 0.8 秒 摇摆层动画周期
摇摆延迟 摇摆 继承全局 延迟;否则 0 秒 摇摆层延迟
颤抖幅度 颤抖 0.09 em 颤抖位移幅度
颤抖角度 颤抖 6 deg 颤抖旋转角度
颤抖速度 颤抖 继承全局 速度;否则 0.18 秒 颤抖层动画周期
颤抖延迟 颤抖 继承全局 延迟;否则 0 秒 颤抖层延迟
缩放倍数 缩放 1.1 — 缩放倍数(无单位)
缩放速度 缩放 继承全局 速度;否则 0.8 秒 缩放层动画周期
缩放延迟 缩放 继承全局 延迟;否则 0 秒 缩放层延迟

数值解析

  • 幅度参数仅接受纯数字;空值或非数字时回退为程序内置默认值(见上表)。
  • 单位由模块固定(em / deg / 无),无需(也不应)在数字后追加单位。
  • 速度/延迟 及模式专属版本单位为秒。

错误处理

  • 未知模式:被忽略;全部未知时回退为 颤抖。
  • 非法数值:回退为该参数的默认值。
  • 内容为空:输出空字符串。

-- 模块文档见 [[Module:抖动/doc]]
local p = {}

-- 模式 → 类前缀 + 数值参数名 + 默认数值
local modeDef = {
	['平移'] = { pre = 'translate', numParam = '平移幅度', def = '0.11' },
	['旋转'] = { pre = 'rotate',    numParam = '旋转角度', def = '9' },
	['跳动'] = { pre = 'bounce',    numParam = '跳动高度', def = '0.17' },
	['摇摆'] = { pre = 'swing',     numParam = '摇摆角度', def = '9' },
	['颤抖'] = { pre = 'jitter',    numParam = '颤抖幅度', def = '0.09' },
	['缩放'] = { pre = 'pulse',     numParam = '缩放倍数', def = '1.1' },
}

-- 颤抖角度参数(颤抖为位移 + 角度两个变量)
local JITTER_ANGLE = '颤抖角度'
local JITTER_DEF_ANGLE = '6'

-- 解析数值:非法/空 → 默认值
local function numOf(v, def)
	if v == nil then return def end
	v = tostring(v):match('^%s*(.-)%s*$')
	if v == nil or v == '' then return def end
	local n = tonumber(v)
	if not n then return def end
	return tostring(n)
end

-- 模式拆分
local function splitModes(s)
	if not s or s == '' then return { '颤抖' } end
	local out, seen = {}, {}
	for m in mw.ustring.gmatch(s, '[^%+、,, ]+') do
		if modeDef[m] and not seen[m] then
			seen[m] = true
			out[#out + 1] = m
		end
	end
	if #out == 0 then return { '颤抖' } end
	return out
end

-- 生成该模式的行内 CSS 变量声明
local function varDecls(m, args)
	local def = modeDef[m]
	local parts = {}
	if m == '颤抖' then
		parts[#parts + 1] = '--dk-amp:' .. numOf(args[def.numParam], def.def) .. 'em'
		parts[#parts + 1] = '--dk-angle:' .. numOf(args[JITTER_ANGLE], JITTER_DEF_ANGLE) .. 'deg'
	elseif m == '跳动' then
		parts[#parts + 1] = '--dk-amp:-' .. numOf(args[def.numParam], def.def) .. 'em'
	elseif m == '缩放' then
		parts[#parts + 1] = '--dk-scale:' .. numOf(args[def.numParam], def.def)
	elseif m == '平移' then
		parts[#parts + 1] = '--dk-amp:' .. numOf(args[def.numParam], def.def) .. 'em'
	else -- 旋转 / 摇摆
		parts[#parts + 1] = '--dk-angle:' .. numOf(args[def.numParam], def.def) .. 'deg'
	end
	return parts
end

-- 嵌套包裹
local function wrap(inner, modes, info, perChar, idx)
	local html = inner
	for i = #modes, 1, -1 do
		local m = modes[i]
		local cls = 'dk-' .. modeDef[m].pre
		local parts = {}
		for _, v in ipairs(info.vars[i]) do parts[#parts + 1] = v end
		for _, pt in ipairs(info.styles[i]) do parts[#parts + 1] = pt end
		if perChar and i == 1 then
			local filtered = {}
			for _, pt in ipairs(parts) do
				if not mw.ustring.match(pt, '^animation%-delay:') then
					filtered[#filtered + 1] = pt
				end
			end
			parts = filtered
			local d = string.format('%.2f', ((idx - 1) * 0.12) % 0.5)
			table.insert(parts, 1, 'animation-delay:-' .. d .. 's')
			cls = 'shake-char ' .. cls
		end
		local attr = ''
		if #parts > 0 then attr = ' style="' .. table.concat(parts, ';') .. '"' end
		html = '<span class="' .. cls .. '"' .. attr .. '>' .. html .. '</span>'
	end
	return html
end

function p.main(frame)
	local args = frame.args
	local text = args['内容'] or ''
	local perChar = (args['逐字'] or '否') == '是'
	local gDur, gDel = args['速度'], args['延迟']
	local modes = splitModes(args['模式'])

	local info = { vars = {}, styles = {} }
	for i, m in ipairs(modes) do
		info.vars[i] = varDecls(m, args)
		local parts = {}
		local dur = args[m .. '速度']
		if (dur == nil or dur == '') and gDur ~= nil and gDur ~= '' then dur = gDur end
		if dur ~= nil and dur ~= '' then parts[#parts + 1] = 'animation-duration:' .. dur .. 's' end
		local del = args[m .. '延迟']
		if (del == nil or del == '') and gDel ~= nil and gDel ~= '' then del = gDel end
		if del ~= nil and del ~= '' then parts[#parts + 1] = 'animation-delay:' .. del .. 's' end
		info.styles[i] = parts
	end

	if perChar then
		local out, i = {}, 0
		for ch in mw.ustring.gmatch(text, '.') do
			if ch == ' ' then
				out[#out + 1] = ' '
			else
				i = i + 1
				out[#out + 1] = wrap(mw.text.encode(ch), modes, info, true, i)
			end
		end
		return '<span class="shake-chars">' .. table.concat(out) .. '</span>'
	else
		return wrap(frame:preprocess(text), modes, info, false, 1)
	end
end

return p