打开/关闭菜单
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
登录后可编辑和发表评论。

Module:SVG Data URL

来自Vocawiki

本模块使得可以在页面中嵌入SVG图像。用途包括:实现随参数变化的内容;使用currentColor[1]

Data URL模式

本站没有启用<svg>标签,显示SVG的原理是使用Data URL<img>提供源。

用法

参数svg填SVG代码。前面不要填<?xml …<!DOCTYPE svg …,直接以<svg开头。

没指定xmlns的话,会自动加上xmlns="http://www.w3.org/2000/svg"

{{svg|svg=
<svg width="300" height="200">
  <rect width="100%" height="100%" fill="darkturquoise"/>
  <circle cx="150" cy="100" r="80" fill="cadetblue"/>
  <text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG</text>
</svg>
}}

<img srcset="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='300'%20height='200'%3E%3Crect%20width='100%25'%20height='100%25'%20fill='darkturquoise'/%3E%3Ccircle%20cx='150'%20cy='100'%20r='80'%20fill='cadetblue'/%3E%3Ctext%20x='150'%20y='125'%20font-size='60'%20text-anchor='middle'%20fill='white'%3ESVG%3C/text%3E%3C/svg%3E" alt="SVG">

svgsrcsrcset以外的任何参数,如widthaltclassstyle等,会被原封不动地添加到<img>上。不需要加引号。

{{svg
|width=100
|style=border-radius:6px; box-shadow: 0 2px 6px rgb(0 0 0 / 0.5);
|alt=表情
|svg=
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 93.1 81.3">
  <path d="M93.1,44.8v36.5H4.6C0,64.1-.3,51.9,.1,34.3c0-5.9,.3-17.6,5.8-21.7,7,.7,9.8,7.5,16,9.6,9.7-3.7,20.1-4.6,30.1-4.8C58,9.9,62.1,1.5,71.6,0c5.3,2.5,6,7,7.1,11.4,1.6,6.2,2.7,12.5,4.7,18.6,1.9,5.6,3.5,11.8,9.7,14.8Z"/>
  <circle cx="25.8" cy="47.7" r="9" fill="#fff"/>
  <circle cx="58.2" cy="42" r="9" fill="#fff"/>
  <circle cx="22.6" cy="45.6" r="5"/>
  <circle cx="62" cy="43.6" r="5"/>
</svg>
}}

<img srcset="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2093.1%2081.3'%3E%3Cpath%20d='M93.1,44.8v36.5H4.6C0,64.1-.3,51.9,.1,34.3c0-5.9,.3-17.6,5.8-21.7,7,.7,9.8,7.5,16,9.6,9.7-3.7,20.1-4.6,30.1-4.8C58,9.9,62.1,1.5,71.6,0c5.3,2.5,6,7,7.1,11.4,1.6,6.2,2.7,12.5,4.7,18.6,1.9,5.6,3.5,11.8,9.7,14.8Z'/%3E%3Ccircle%20cx='25.8'%20cy='47.7'%20r='9'%20fill='%23fff'/%3E%3Ccircle%20cx='58.2'%20cy='42'%20r='9'%20fill='%23fff'/%3E%3Ccircle%20cx='22.6'%20cy='45.6'%20r='5'/%3E%3Ccircle%20cx='62'%20cy='43.6'%20r='5'/%3E%3C/svg%3E" alt="表情" style="border-radius:6px; box-shadow: 0 2px 6px rgba(0, 0, 0, .3);" width="100">

注意

作为文字的"'需要换为XML实体("&quot;'&apos;),否则两者会颠倒。

{{svg|svg=
<svg width="300" height="100" style="background:#fff">
  <text x="150" y="40" font-size="20" text-anchor="middle">字符:"双引号" '单引号'</text>
  <text x="150" y="80" font-size="20" text-anchor="middle">实体:&quot;双引号&quot; &apos;单引号&apos;</text>
</svg>
}}

<img srcset="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='300'%20height='100'%20style='background:%23fff'%3E%3Ctext%20x='150'%20y='40'%20font-size='20'%20text-anchor='middle'%3E%E5%AD%97%E7%AC%A6%EF%BC%9A'%E5%8F%8C%E5%BC%95%E5%8F%B7'%20%22%E5%8D%95%E5%BC%95%E5%8F%B7%22%3C/text%3E%3Ctext%20x='150'%20y='80'%20font-size='20'%20text-anchor='middle'%3E%E5%AE%9E%E4%BD%93%EF%BC%9A%26quot%3B%E5%8F%8C%E5%BC%95%E5%8F%B7%26quot%3B%20%26apos%3B%E5%8D%95%E5%BC%95%E5%8F%B7%26apos%3B%3C/text%3E%3C/svg%3E" alt="SVG">

CSS模式

当没有使用svg参数时,模板进入CSS模式。原理是使用clip-path: path(...) CSS属性与函数。

用法

{{svg}}参数(标“*”的为必填参数):

tag 最外层元素所用HTML标签。默认为span
width* 宽度,为CSS长度(<length>数据类型)或数字(表示px)
height* 高度,同上。widthheight暂时都是必填,若需自动推导请提出讨论。
viewBox 作用同<svg>viewBox
  • 为了简洁性与一致性,不支持通过逗号分隔;
  • 暂不支持除0以外的min-xmin-y,若有需求请提出讨论。
style CSS样式。
匿名参数1* 内部元素,见下。可以放入多个元素。

可使用的内部元素

{{svg/path}}

d或匿名参数1* SVG路径,见CSS path()函数的参数。
fill 填充,语法同CSS background属性。默认为黑色。
fill-rule 见CSS path()函数的参数。
style CSS样式。

不支持stroke

示例

展示了fill=currentColor

{{svg
| width = 10em | height = 8.733em | viewBox = 0 0 93.1 81.3
| style = color: green; border-radius: 6px; box-shadow: 0 2px 6px rgb(0 0 0 / 0.5);
| {{svg/path
  | M93.1,44.8v36.5H4.6C0,64.1-.3,51.9,.1,34.3c0-5.9,.3-17.6,5.8-21.7,7,.7,9.8,7.5,16,9.6,9.7-3.7,20.1-4.6,30.1-4.8C58,9.9,62.1,1.5,71.6,0c5.3,2.5,6,7,7.1,11.4,1.6,6.2,2.7,12.5,4.7,18.6,1.9,5.6,3.5,11.8,9.7,14.8Z
  | fill = currentColor
  }}
}}

  1. 仅CSS模式支持
local p = {}

local function formSvg(svg)
	assert(mw.ustring.match(svg, '^<svg[%s>]'), 'svg参数填写错误')

	--[[ 缩短长度 ]]
	local SUBSTITUTE = {
		{'%s+', ' '},
		-- 如 <path ... /> -> <path .../>
		{' ?(</?) ?', '%1'},
		{' ?(/?>) ?', '%1'},
		-- 如 <path ...></path> -> <path .../>
		-- ([:%a_][:%w_%-%.]*) - tag name: https://www.w3.org/TR/REC-xml/#NT-Name
		-- ([^<>]*)            - attributes
		{'<([:%a_][:%w_-.]*)([^<>]*)></%1>', '<%1%2/>'},
	}
	for _i, v in ipairs(SUBSTITUTE) do
		svg = mw.ustring.gsub(svg, v[1], v[2])
	end

	-- 添加xmlns
	if not mw.ustring.match(svg, 'xmlns') then
		svg = '<svg xmlns="http://www.w3.org/2000/svg"' .. string.sub(svg, 5)
	end
	return svg
end


local function formImg(src, attrs)
	attrs.alt = attrs.alt or 'SVG'
	local parts = {'<img srcset="', src, '"'}
	for k, v in pairs(attrs) do
		table.insert(parts, string.format(' %s="%s"', k, v))
	end
	table.insert(parts, ">")
	return table.concat(parts)
end


--[[
-- 该函数借鉴了Taylor Hunt的https://github.com/tigt/mini-svg-data-uri
--]]
function p.toDataUrl(str)
	local PATTERN = {
		-- strip marker格式:
		-- \127'"`UNIQ--标签-8个16进制数-QINU`"'\127
		-- 匹配(用PCRE格式表示,看得清楚些):
		-- \127'"`UNIQ--(.+?-.+?)-QINU`"'\127
		stripMarker = '%%7F%%27%%22%%60UNIQ%-%-(.-%-.-)%-QINU%%60%%22%%27%%7F',
		hexEncode = '%%%x%x',
	}
	local ACCEPTABLE = {
		['%22'] = "'",   -- 把编码后"换成未编码的'
		['%27'] = '%22', -- 把编码后'换成编码后的"
		['%28'] = '(',
		['%29'] = ')',
		['%2C'] = ',',
		['%2F'] = '/',
		['%3A'] = ':',
		['%3D'] = '=',
	}
	str = mw.uri.encode(str, 'PATH')
	-- 恢复strip marker
	str = string.gsub(str, PATTERN.stripMarker, '\127\'"`UNIQ--%1-QINU`"\'\127')
	-- 替换部分编码回原字符,减小文件体积
	str = string.gsub(str, PATTERN.hexEncode, ACCEPTABLE)

	return "data:image/svg+xml," .. str
end


function p._main(args)
	local allArgs = {}
	for k, v in pairs(args) do
		allArgs[string.lower(k)] = v
	end

	local svg = formSvg(allArgs.svg)

	local NOT_IMG_ATTR = {
		'svg', -- 并非用于<img>
		'src', 'srcset', 'sizes' -- 不由用户指定
	}
	for _i, attr in ipairs(NOT_IMG_ATTR) do
		allArgs[attr] = nil
	end

	return formImg(p.toDataUrl(svg), allArgs)
end


function p.main(frame)
	local parent = frame:getParent()
	if parent and parent:getTitle() == 'Template:Svg' then
		frame = parent
	end
	return p._main(frame.args)
end

return p