模块:Background:修订间差异
来自Blockland Wiki
更多操作
创建页面,内容为“ local p = {} -- 清理参数 local function trim(value) return mw.text.trim(tostring(value or "")) end -- 透明度限制在 0 到 1 local function opacity(value, default) local number = tonumber(trim(value)) if not number then return default end number = math.max(0, math.min(1, number)) return tostring(number) end -- 只接受安全、常见的图片定位写法 local validPositions = { center = true, top = true, bottom = true, left = true,…” |
无编辑摘要 |
||
| 第2行: | 第2行: | ||
local p = {} | local p = {} | ||
local function trim(value) | local function trim(value) | ||
return mw.text.trim(tostring(value or "")) | return mw.text.trim(tostring(value or "")) | ||
end | end | ||
local function getOpacity(value, default) | |||
local function | |||
local number = tonumber(trim(value)) | local number = tonumber(trim(value)) | ||
if not number then | if not number or number ~= number then | ||
return default | return default | ||
end | end | ||
| 第19行: | 第17行: | ||
end | end | ||
local positionNames = { | |||
local | |||
center = true, | center = true, | ||
top = true, | top = true, | ||
bottom = true, | bottom = true, | ||
left = true, | left = true, | ||
right = true, | right = true | ||
} | |||
local validPositions = { | |||
center = true, | |||
["center center"] = true, | |||
["center top"] = true, | ["center top"] = true, | ||
["center bottom"] = true, | ["center bottom"] = true, | ||
| 第34行: | 第36行: | ||
["left bottom"] = true, | ["left bottom"] = true, | ||
["right bottom"] = true, | ["right bottom"] = true, | ||
top = true, | |||
bottom = true, | |||
left = true, | |||
right = true | |||
} | } | ||
local function validPositionToken( | local function validPositionToken(value) | ||
if | if positionNames[value] then | ||
return true | return true | ||
end | end | ||
if | if value == "0" then | ||
return true | return true | ||
end | end | ||
return | return value:match("^%-?%d+%.?%d*%%$") ~= nil | ||
or value:match("^%-?%d+%.?%d*px$") ~= nil | |||
end | end | ||
local function | local function getPosition(value) | ||
value = trim(value) | value = trim(value) | ||
| 第67行: | 第73行: | ||
end | end | ||
local colorNames = { | local colorNames = { | ||
white = true, | white = true, | ||
| 第81行: | 第86行: | ||
} | } | ||
local function | local function getColor(value) | ||
value = trim(value) | value = trim(value) | ||
if value:match("^#%x%x%x$") | if value:match("^#%x%x%x$") | ||
value:match("^#%x%x%x%x%x%x$") then | or value:match("^#%x%x%x%x%x%x$") then | ||
return value | return value | ||
end | end | ||
| 第98行: | 第103行: | ||
end | end | ||
-- | -- 支持直接 URL、普通文件名,以及被展开成 HTML 的文件链接。 | ||
local function getImageSource(frame, | local function getImageSource(frame, value) | ||
value = trim(value) | |||
if | if value == "" then | ||
return "" | return "" | ||
end | end | ||
-- | -- 如果模板参数已被展开成图片 HTML,提取其 src。 | ||
local embedded = | |||
return | value:match('<img[^>]-src="([^"]+)"') | ||
or value:match("<img[^>]-src='([^']+)'") | |||
if embedded then | |||
return embedded:gsub("&", "&") | |||
end | end | ||
-- | -- 去除尚未展开的 Wiki 文件链接标记及显示参数。 | ||
value = value:gsub("^%[%[", "") | |||
value = value:gsub("%]%]$", "") | |||
value = value:match("^([^|]+)") or value | |||
value = trim(value) | |||
-- 外部图片地址。 | |||
if value:match("^https?://%S+$") | |||
or value:match("^//%S+$") then | |||
return value | |||
end | |||
-- 已经是站内图片路径。 | |||
if value:match("^/images/") | |||
or value:match("^/wiki/Special:FilePath/") then | |||
return value | |||
end | |||
-- 转换成纯文件名,不要把文件链接语法交给 filepath。 | |||
value = value:gsub("^[Ff][Ii][Ll][Ee]:", "") | |||
value = value:gsub("^[Ii][Mm][Aa][Gg][Ee]:", "") | |||
value = value:gsub("^文件:", "") | |||
value = value:gsub("^图像:", "") | |||
value = trim(value) | |||
local ok, result = pcall(function() | local ok, result = pcall(function() | ||
return frame:callParserFunction("filepath", | return frame:callParserFunction("filepath", value) | ||
end) | end) | ||
if ok and result | if ok and result then | ||
result = trim(result) | |||
if result ~= "" | |||
and not result:find("<", 1, true) | |||
and not result:find(">", 1, true) then | |||
return result | |||
end | |||
end | end | ||
| 第129行: | 第164行: | ||
function p.main(frame) | function p.main(frame) | ||
local parent = frame:getParent() | local parent = frame:getParent() | ||
local args = | local args = parent and parent.args or frame.args | ||
local image = args["图片"] | |||
or args["url"] | |||
or args[1] | |||
or "" | |||
local src = getImageSource(frame, image) | local src = getImageSource(frame, image) | ||
local imageOpacity = | local imageOpacity = getOpacity( | ||
local imagePosition = | args["透明度"] or args["opacity"], | ||
1 | |||
) | |||
local imagePosition = getPosition( | |||
args["位置"] or args["position"] or "center" | |||
) | |||
local baseColor = getColor( | |||
args["背景色"] or args["颜色"] or args["color"] | |||
) | |||
local | local shadeColor = getColor( | ||
args["遮罩颜色"] or args["shadeColor"] | |||
local shadeOpacity = | ) | ||
local shadeOpacity = getOpacity( | |||
args["遮罩透明度"] or args["shade"], | |||
0 | |||
) | |||
if src == "" and baseColor == "" then | if src == "" and baseColor == "" then | ||
return "" | return "" | ||
end | end | ||
-- | -- 整个背景位于较低层级,不应截获鼠标事件。 | ||
local rootStyle = | local rootStyle = | ||
"position:fixed;" .. | "position:fixed;" .. | ||
| 第154行: | 第207行: | ||
"height:100%;" .. | "height:100%;" .. | ||
"z-index:-1;" .. | "z-index:-1;" .. | ||
"overflow:hidden;" | "overflow:hidden;" .. | ||
"pointer-events:none;" | |||
if baseColor ~= "" then | if baseColor ~= "" then | ||
rootStyle = rootStyle .. "background-color:" .. baseColor .. ";" | rootStyle = rootStyle .. | ||
"background-color:" .. baseColor .. ";" | |||
end | end | ||
| 第165行: | 第220行: | ||
:cssText(rootStyle) | :cssText(rootStyle) | ||
-- | -- 第一层:背景图片。 | ||
if src ~= "" then | if src ~= "" then | ||
local img = mw.html.create("img") | local img = mw.html.create("img") | ||
| 第177行: | 第232行: | ||
"width:100%;" .. | "width:100%;" .. | ||
"height:100%;" .. | "height:100%;" .. | ||
"max-width:none;" .. | |||
"object-fit:cover;" .. | "object-fit:cover;" .. | ||
"object-position:" .. imagePosition .. ";" .. | "object-position:" .. imagePosition .. ";" .. | ||
"opacity:" .. imageOpacity .. ";" | "opacity:" .. imageOpacity .. ";" .. | ||
"pointer-events:none;" | |||
) | ) | ||
-- 关键:插入 HTML 节点,不再使用 wikitext(tostring(img))。 | |||
root:node(img) | |||
end | end | ||
-- | -- 第二层:颜色遮罩,用来降低背景的视觉干扰。 | ||
if shadeColor ~= "" and tonumber(shadeOpacity) > 0 then | if shadeColor ~= "" | ||
and tonumber(shadeOpacity) > 0 then | |||
local shade = mw.html.create("div") | local shade = mw.html.create("div") | ||
:addClass("bl-page-bg-shade") | :addClass("bl-page-bg-shade") | ||
| 第196行: | 第256行: | ||
"height:100%;" .. | "height:100%;" .. | ||
"background-color:" .. shadeColor .. ";" .. | "background-color:" .. shadeColor .. ";" .. | ||
"opacity:" .. shadeOpacity .. ";" | "opacity:" .. shadeOpacity .. ";" .. | ||
"pointer-events:none;" | |||
) | ) | ||
root: | root:node(shade) | ||
end | end | ||
2026年10月9日 (五) 23:13的最新版本
此模块的文档可以在模块:Background/doc创建
local p = {}
local function trim(value)
return mw.text.trim(tostring(value or ""))
end
local function getOpacity(value, default)
local number = tonumber(trim(value))
if not number or number ~= number then
return default
end
number = math.max(0, math.min(1, number))
return tostring(number)
end
local positionNames = {
center = true,
top = true,
bottom = true,
left = true,
right = true
}
local validPositions = {
center = true,
["center center"] = true,
["center top"] = true,
["center bottom"] = true,
["left center"] = true,
["right center"] = true,
["left top"] = true,
["right top"] = true,
["left bottom"] = true,
["right bottom"] = true,
top = true,
bottom = true,
left = true,
right = true
}
local function validPositionToken(value)
if positionNames[value] then
return true
end
if value == "0" then
return true
end
return value:match("^%-?%d+%.?%d*%%$") ~= nil
or value:match("^%-?%d+%.?%d*px$") ~= nil
end
local function getPosition(value)
value = trim(value)
if validPositions[value] then
return value
end
local x, y = value:match("^(%S+)%s+(%S+)$")
if x and y
and validPositionToken(x)
and validPositionToken(y) then
return x .. " " .. y
end
return "center center"
end
local colorNames = {
white = true,
black = true,
transparent = true,
red = true,
blue = true,
green = true,
yellow = true,
gray = true,
grey = true,
navy = true
}
local function getColor(value)
value = trim(value)
if value:match("^#%x%x%x$")
or value:match("^#%x%x%x%x%x%x$") then
return value
end
local lower = value:lower()
if colorNames[lower] then
return lower
end
return ""
end
-- 支持直接 URL、普通文件名,以及被展开成 HTML 的文件链接。
local function getImageSource(frame, value)
value = trim(value)
if value == "" then
return ""
end
-- 如果模板参数已被展开成图片 HTML,提取其 src。
local embedded =
value:match('<img[^>]-src="([^"]+)"')
or value:match("<img[^>]-src='([^']+)'")
if embedded then
return embedded:gsub("&", "&")
end
-- 去除尚未展开的 Wiki 文件链接标记及显示参数。
value = value:gsub("^%[%[", "")
value = value:gsub("%]%]$", "")
value = value:match("^([^|]+)") or value
value = trim(value)
-- 外部图片地址。
if value:match("^https?://%S+$")
or value:match("^//%S+$") then
return value
end
-- 已经是站内图片路径。
if value:match("^/images/")
or value:match("^/wiki/Special:FilePath/") then
return value
end
-- 转换成纯文件名,不要把文件链接语法交给 filepath。
value = value:gsub("^[Ff][Ii][Ll][Ee]:", "")
value = value:gsub("^[Ii][Mm][Aa][Gg][Ee]:", "")
value = value:gsub("^文件:", "")
value = value:gsub("^图像:", "")
value = trim(value)
local ok, result = pcall(function()
return frame:callParserFunction("filepath", value)
end)
if ok and result then
result = trim(result)
if result ~= ""
and not result:find("<", 1, true)
and not result:find(">", 1, true) then
return result
end
end
return ""
end
function p.main(frame)
local parent = frame:getParent()
local args = parent and parent.args or frame.args
local image = args["图片"]
or args["url"]
or args[1]
or ""
local src = getImageSource(frame, image)
local imageOpacity = getOpacity(
args["透明度"] or args["opacity"],
1
)
local imagePosition = getPosition(
args["位置"] or args["position"] or "center"
)
local baseColor = getColor(
args["背景色"] or args["颜色"] or args["color"]
)
local shadeColor = getColor(
args["遮罩颜色"] or args["shadeColor"]
)
local shadeOpacity = getOpacity(
args["遮罩透明度"] or args["shade"],
0
)
if src == "" and baseColor == "" then
return ""
end
-- 整个背景位于较低层级,不应截获鼠标事件。
local rootStyle =
"position:fixed;" ..
"top:0;" ..
"left:0;" ..
"width:100%;" ..
"height:100%;" ..
"z-index:-1;" ..
"overflow:hidden;" ..
"pointer-events:none;"
if baseColor ~= "" then
rootStyle = rootStyle ..
"background-color:" .. baseColor .. ";"
end
local root = mw.html.create("div")
:addClass("bl-page-bg-layer")
:attr("aria-hidden", "true")
:cssText(rootStyle)
-- 第一层:背景图片。
if src ~= "" then
local img = mw.html.create("img")
:addClass("bl-page-bg-image")
:attr("src", src)
:attr("alt", "")
:cssText(
"position:absolute;" ..
"top:0;" ..
"left:0;" ..
"width:100%;" ..
"height:100%;" ..
"max-width:none;" ..
"object-fit:cover;" ..
"object-position:" .. imagePosition .. ";" ..
"opacity:" .. imageOpacity .. ";" ..
"pointer-events:none;"
)
-- 关键:插入 HTML 节点,不再使用 wikitext(tostring(img))。
root:node(img)
end
-- 第二层:颜色遮罩,用来降低背景的视觉干扰。
if shadeColor ~= ""
and tonumber(shadeOpacity) > 0 then
local shade = mw.html.create("div")
:addClass("bl-page-bg-shade")
:cssText(
"position:absolute;" ..
"top:0;" ..
"left:0;" ..
"width:100%;" ..
"height:100%;" ..
"background-color:" .. shadeColor .. ";" ..
"opacity:" .. shadeOpacity .. ";" ..
"pointer-events:none;"
)
root:node(shade)
end
return tostring(root)
end
return p