此模块用于实现模板链接:{{Pie chart}}模板,该模板用于实现一个饼图。
此模板的函数module
用于直接调用,template
用于由模板调用。此外,_main
可直接传入args
表,这个args
表中可以直接指定data
字段,其值为表。例如,下面两段代码是等价的:
-- p 表示模块的返回值。
p._main {
label1 = '第1项',
value1 = '23', -- 这个值也可以是直接的数字形式
label2 = '第2项',
value2 = 40
}
p._main {
data = {
{label = '第1项', value = '23'},
{label = '第2项', value = '40'}
}
}
另请参见Module:TableTools。
local p = {}
local tools = require 'Module:TableTools'
local yesno = require 'Module:Yesno'
local graphColor = {
'#1F78B4',
'#33A02C',
'#E31A1C',
'#FF7F00',
'#6A3D9A',
'#B15928',
'#A6CEE3',
'#B2DF8A',
'#FB9A99',
'#FDBF6F',
'#CAB2D6',
'#FFFF99',
'#FEEBE2',
'#A9A9A9',
}
function p._main(args, frame)
local circleOnly = yesno(args.circleOnly)
-- 图形部分
local circle = mw.html.create 'div'
:addClass 'PieChartCircle'
local root = circleOnly and circle or mw.html.create 'div'
:addClass 'PieChartTemplate thumb'
:attr('aria-label', '饼图')
local radius = args.radius -- 这个值可以是字母或者数字,如果是数字,则不执行trim,以避免转换为string再转换为number的多余过程
radius = type(radius) == 'string' and mw.text.trim(args.radius) or radius
radius = radius ~= '' and radius or '100px'
-- 参数radius应该是个css长度,如果不是,则自动加上单位
if tonumber(radius) then
radius = radius .. 'px'
end
root:css('--PieChartRadius', radius)
local data = type(args.data) == 'table' and args.data or tools.numData(args, true) -- 第二个参数表示 compress
local from = args.from
from = type(from) == 'string' and mw.text.trim(from) or from
from = from ~= '' and from or nil
if tonumber(from) then
from = 'calc(' .. from .. ' / var(--PieChartTotalValue) * 1turn)'
end
circle:css('--PieChartPiecesStart', from)
local space = args.space and mw.text.trim(args.space) -- 各项之间的间距,其类型为角度
space = space ~= '' and space or nil
if tonumber(space) then
mw.addWarning(
'调用[[Module:Pie chart]]吋的警告:参数space的值不能是数字(' .. tostring(space) .. '),而应该是一个CSS的角度值,例如10deg、0.05turn等')
space = nil
end
circle:css('--PieChartSpace', space)
local offset = args.offset and mw.text.trim(args.offset)
offset = offset ~= '' and offset or nil
if tonumber(offset) then
mw.addWarning(
'调用[[Module:Pie chart]]时的警告:参数offset的值不能是数字(' .. tostring(offset) .. '),而应该是一个CSS的长度值,例如10px、0.5em等')
end
circle:css('--PieChartPieceOffset', offset)
circle:tag 'div'
:addClass 'PieChartHiddenText'
:wikitext ('饼图,共' .. (#data) .. '个元素。')
local stackedValue = 0
local stackedSpaceNum = 0
-- 计算total
local total = tonumber(args.total)
if args.total and not total then
error ('调用[[Module:Pie chart]]时出现错误:参数total的值(' .. tostring(args.total) .. ')不是一个有效的数字')
end
if not total then
total = 0
for i, item in ipairs(data) do
local value = tonumber(item.value) or 1
if item.value and not value then
error ('调用[[Module:PieChart]]时出现错误:' .. tostring(item.value) .. '不是一个有效的数字')
end
total = total + value
end
end
for i, item in ipairs(data) do
local value = tonumber(item.value) or 1
local color = item.color or graphColor[i % #graphColor]
local beginAngle = space and string.format(
'calc((%s / var(--PieChartTotalValue)) * (1turn - var(--PieChartTotalSpace)) + %s * var(--PieChartSpace))',
stackedValue, stackedSpaceNum) or string.format('calc((%s / var(--PieChartTotalValue)) * 1turn)', stackedValue)
local endAngle = space and string.format(
'calc((%s / var(--PieChartTotalValue)) * (1turn - var(--PieChartTotalSpace)) + %s * var(--PieChartSpace))',
stackedValue + value, stackedSpaceNum) or string.format('calc((%s / var(--PieChartTotalValue)) * 1turn)', stackedValue + value)
local piece = circle:tag 'div'
:addClass 'PieChartPiece mw-no-invert'
:css('--PieChartPieceBeginAngle', beginAngle)
:css('--PieChartPieceEndAngle', endAngle)
:css('--PieChartPieceColor', color)
piece :tag 'div'
:addClass 'PieChartPieceHiddenText'
:wikitext('饼图元素,第' .. i .. '项,' ..
(item.label and ('标签内容:' .. mw.text.trim(item.label) .. ',') or '') ..
('数值:' .. value) .. ',百分比:' .. (value / total * 100) .. '%。')
if offset then
piece:addClass 'PieChartPieceOffset'
end
piece:css('--PieChartRadius', item.radius)
piece:css('--PieChartPieceOffset', item.offset)
stackedValue = stackedValue + value
stackedSpaceNum = stackedSpaceNum + 1
end
if yesno(args.other) then
data[#data + 1] = {
label = '其他',
value = total - stackedValue
}
end
circle:css{
['--PieChartTotalValue'] = total,
['--PieChartTotalSpace'] = space and 'calc(' .. stackedSpaceNum .. ' * var(--PieChartSpace))' or nil,
}
if circleOnly then
-- 仅返回中间的圆形的部分
return circle
end
local thumb = args.thumb and mw.text.trim(args.thumb)
if not thumb or thum == '' then
thumb = 'right'
end
if thumb == 'center' then
root:addClass 'tnone'
elseif thumb == 'none' or thumb == 'left' or thumb == 'right' then
root:addClass('t' .. thumb)
else
error ("调用[[Module:Pie chart]]出现错误:未知的thumb参数:" .. tostring(thumb) .. ",可接受的参数包括:left、center、right")
end
root:cssText(args.style)
local thumbinner = root:tag 'div'
:addClass 'thumbinner'
thumbinner:node(circle)
local caption = args.caption and mw.text.trim(args.caption)
caption = caption ~= '' and caption or nil
local legends = yesno(args.legends, true) ~= false -- 未指定时为 true
local footer = args.footer and mw.text.trim(args.footer)
footer = footer ~= '' and footer or nil
if caption or legends or footer then
local thumbCaption = thumbinner:tag 'div'
:addClass 'thumbcaption'
if caption then
thumbCaption:wikitext(args.caption):newline()
end
if legends then
local g_info = args.info and mw.text.trim(args.info) or 'percentage'
for i, item in ipairs(data) do
local value = tonumber(item.value) or 1
local color = item.color or graphColor[i % #graphColor]
local info = (item.info and mw.text.trim(item.info)) or g_info
assert(frame, '未提供参数:frame')
local infoContent
if info == 'value' then
infoContent = value
elseif info == 'percentage' then
infoContent = (value / total * 100) .. '%'
end
local label = item.label
if label and infoContent then
label = label .. '(' .. infoContent .. ')'
elseif infoContent then
label = infoContent
end
if label then
local legend = frame:expandTemplate{
title = 'Template:Legend',
args = {
color,
label
}
}
thumbCaption:wikitext(legend)
end
end
end
if footer then
thumbCaption:tag 'div'
:addClass 'PieChartFooter'
:newline()
:wikitext(footer)
:newline()
end
end
return root
end
function p.module(frame)
return p._main(frame.args, frame)
end
function p.template(frame)
return p.module(frame:getParent())
end
return p