diff --git a/public/demo.json.conf b/public/demo.json.conf index 5dcd887..1cb16c3 100644 --- a/public/demo.json.conf +++ b/public/demo.json.conf @@ -57,7 +57,7 @@ "centerIcon": { "rIcon": 50, -- 图标半径,单位:像素 "opacity": 0.8, -- 图标透明度(0.0-1.0,0为完全透明,1为完全不透明) - "name": "taiji.svg" -- SVG图标文件名,路径固定为 /icons/ 目录 + "name": "centericon.svg" -- SVG图标文件名,路径固定为 /icons/ 目录 }, -- ======================================== @@ -83,13 +83,14 @@ -- 从内向外定义每一层的配置 "layers": [ -- ======================================== - -- 第1层:阴阳 (2等分) + -- 阴阳 (2等分) -- ======================================== -- 演示:扇区级别 colorRef 优先级最高 { "divisions": 2, -- 2等分 "rInner": 60, -- 内半径 "rOuter": 90, -- 外半径 + "startAngle": 0, -- 第一个扇区的起始角度(度,0度为正北方向,顺时针) -- "colorPalette": "Black", -- 默认配色(已废弃,已注释) "sectors": [ { @@ -105,31 +106,14 @@ }, -- ======================================== - -- 第2层:四象 (4等分) - -- ======================================== - -- 演示:旧的 colorMode 方式(将被移除) - { - "divisions": 4, - "rInner": 90, - "rOuter": 120, - -- "colorPalette": "Fire", -- 旧方式:使用 colorPalette(已废弃,已注释) - -- "colorMode": "alternating", -- 旧方式:交替色模式(已废弃,已注释) - "sectors": [ - { "content": "少阳", "innerFill": 1 }, - { "content": "太阳", "innerFill": 0 }, - { "content": "少阴", "innerFill": 1 }, - { "content": "太阴", "innerFill": 0 } - ] - }, - - -- ======================================== - -- 第3层:八卦 (8等分) + -- 八卦 (8等分) -- ======================================== -- 演示:旧的 colorMode 方式(将被移除) { "divisions": 8, "rInner": 120, "rOuter": 160, + "startAngle": 0, -- 第一个扇区的起始角度(度,0度为正北方向,顺时针) -- "colorPalette": "Wood", -- 旧方式:使用 colorPalette -- "colorMode": "gradient", -- 旧方式:渐变模式(将被移除) "sectors": [ @@ -145,18 +129,19 @@ }, -- ======================================== - -- 第4层:地支 (12等分) + -- 地支 (12等分) -- ======================================== -- 演示:规律填色 - 3个着色,1个间隔 - -- 着色规律:start=2, num=3, interval=1 - -- 效果:扇区2-4着色,5空白,6-8着色,9空白,10-12着色,1空白 + -- 着色规律:num=3, interval=1 + -- 效果:扇区1-3着色,4空白,5-7着色,8空白,9-11着色,12空白 { "divisions": 12, "rInner": 160, "rOuter": 200, + "startAngle": 0, -- 第一个扇区的起始角度(度,0度为正北方向,顺时针) "colorRef": "土", -- 着色使用的颜色引用 "innerFill": 1, -- 着色区域的内缩设置 - "start": 2, -- 从第2个扇区开始着色(1-based索引) + -- "start": 1, -- 从第1个扇区开始着色(已废弃,统一从第1个扇区开始) "num": 3, -- 连续着色3个扇区 "interval": 1, -- 着色后间隔1个扇区 "sectors": [ @@ -173,123 +158,94 @@ { "content": "戌" }, { "content": "亥", "innerFill": 0 } ] - }, - + } + -- ======================================== - -- 第5层:二十四山 (24等分) + -- 24等分多文本单元示例 -- ======================================== - -- 演示:规律填色 - 2个着色,2个间隔 - -- 着色规律:start=1, num=2, interval=2 - -- 效果:扇区1-2着色,3-4空白,5-6着色,7-8空白…… + -- 演示:扇区内使用"|"分隔多个文本单元 + -- 文本布局:各单元角度按 LAYOUT_RATIO [0.25, 0.5, 0.25] 分配 + -- 字体大小和位置自动计算,单元内无分割线,通过全局变量LAYOUT_RATIO,可以计算3个单元的角度。通过现在的工具函数,可以计算得到字体大小及排列方式。 { "divisions": 24, - "rInner": 200, - "rOuter": 250, - "colorRef": "水", -- 使用"水"配色 - "innerFill": 0, -- 不显示内缩 - "start": 1, -- 从第1个扇区开始 - "num": 2, -- 连续着色2个扇区 - "interval": 2, -- 着色后间隔2个扇区 + "rInner": 210, + "rOuter": 240, + "startAngle": 0, "sectors": [ - { "content": "壬" }, { "content": "子" }, { "content": "癸" }, - { "content": "丑" }, { "content": "艮" }, { "content": "寅" }, - { "content": "甲" }, { "content": "卯" }, { "content": "乙" }, - { "content": "辰" }, { "content": "巽" }, { "content": "巳" }, - { "content": "丙" }, { "content": "午" }, { "content": "丁" }, - { "content": "未" }, { "content": "坤" }, { "content": "申" }, - { "content": "庚" }, { "content": "酉" }, { "content": "辛" }, - { "content": "戌" }, { "content": "乾" }, { "content": "亥" } + { "content": "甲乙|子|丙丁", "colorRef": "木", "innerFill": 1 }, -- 3个文本单元:左25%"甲乙",中50%"子",右25%"丙丁" + { "content": "丑" }, + { "content": "戊己|寅|庚辛", "colorRef": "土", "innerFill": 0 }, + { "content": "卯" }, + { "content": "壬癸|辰|甲乙", "innerFill": 1 }, + { "content": "巳" }, + { "content": "丙丁|午|戊己", "colorRef": "火", "innerFill": 0 }, + { "content": "未" }, + { "content": "庚辛|申|壬癸", "colorRef": "金", "innerFill": 1 }, + { "content": "酉" }, + { "content": "甲乙|戌|丙丁", "innerFill": 0 }, + { "content": "亥" }, + { "content": "戊己|子|庚辛" }, + { "content": "丑" }, + { "content": "壬癸|寅|甲乙" }, + { "content": "卯" }, + { "content": "丙丁|辰|戊己" }, + { "content": "巳" }, + { "content": "庚辛|午|壬癸" }, + { "content": "未" }, + { "content": "甲乙|申|丙丁" }, + { "content": "酉" }, + { "content": "戊己|戌|庚辛" }, + { "content": "亥" } ] }, - + -- ======================================== - -- 第6层:五行 (5等分) + -- 16等分4个文本单元示例 -- ======================================== - -- 演示:每个扇区单独指定 colorRef(优先级最高) - { - "divisions": 5, - "rInner": 250, - "rOuter": 300, - "sectors": [ - { "content": "木", "colorRef": "Wood", "innerFill": 1 }, -- 单独指定木色 - { "content": "火", "colorRef": "Fire", "innerFill": 1 }, -- 单独指定火色 - { "content": "土", "colorRef": "Earth", "innerFill": 0 }, -- 单独指定土色 - { "content": "金", "colorRef": "Metal", "innerFill": 1 }, -- 单独指定金色 - { "content": "水", "colorRef": "Water", "innerFill": 1 } -- 单独指定水色 - ] - }, - - -- ======================================== - -- 第7层:8等分示例 - -- ======================================== - -- 演示:规律填色 - 交替着色(1个着色,1个间隔) - -- 着色规律:start=1, num=1, interval=1 - -- 效果:奇数扇区着色,偶数扇区空白 - { - "divisions": 8, - "rInner": 300, - "rOuter": 360, - "colorRef": "热", -- 使用"热"配色(暖橙色) - "innerFill": 1, -- 显示内缩 - "start": 1, -- 从第1个扇区开始 - "num": 1, -- 连续着色1个扇区 - "interval": 1, -- 着色后间隔1个扇区(形成交替效果) - "sectors": [ - { "content": "1" }, { "content": "2" }, { "content": "3" }, - { "content": "4" }, { "content": "5" }, { "content": "6" }, - { "content": "7" }, { "content": "8" } - ] - }, - - -- ======================================== - -- 第8层:16等分示例 - -- ======================================== - -- 演示:规律填色 - 4个着色,2个间隔,从第3个开始 - -- 着色规律:start=3, num=4, interval=2 - -- 效果:扇区3-6着色,7-8空白,9-12着色,13-14空白,15-18着色…… + -- 演示:扇区内使用"|"分隔4个文本单元,每个单元2字 + -- 文本布局:4个单元按全局配置LAYOUT_RATIO_PRESETS [0.2, 0.3, 0.3, 0.2] 分配,中间两个大 + -- 每个扇区22.5度,字体大小和位置自动计算,单元内无分割线 { "divisions": 16, - "rInner": 360, - "rOuter": 430, - "colorRef": "冷", -- 使用"冷"配色(冷蓝色) - "innerFill": 0, -- 不显示内缩 - "start": 3, -- 从第3个扇区开始 - "num": 4, -- 连续着色4个扇区 - "interval": 2 -- 着色后间隔2个扇区 + "rInner": 240, + "rOuter": 270, + "startAngle": 0, + "sectors": [ + { "content": "甲乙|丙丁|戊己|庚辛", "colorRef": "木", "innerFill": 1 }, -- 4个文本单元:[20%, 30%, 30%, 20%] + { "content": "壬癸|甲乙|丙丁|戊己", "colorRef": "土", "innerFill": 0 }, + { "content": "庚辛|壬癸|甲乙|丙丁", "innerFill": 1 }, + { "content": "戊己|庚辛|壬癸|甲乙", "colorRef": "火", "innerFill": 0 }, + { "content": "丙丁|戊己|庚辛|壬癸", "colorRef": "金", "innerFill": 1 }, + { "content": "子丑|寅卯|辰巳|午未", "innerFill": 0 }, + { "content": "申酉|戌亥|子丑|寅卯", "colorRef": "木", "innerFill": 1 }, + { "content": "辰巳|午未|申酉|戌亥", "colorRef": "水", "innerFill": 0 }, + { "content": "甲乙|丙丁|戊己|庚辛", "innerFill": 1 }, + { "content": "壬癸|甲乙|丙丁|戊己", "colorRef": "土", "innerFill": 0 }, + { "content": "庚辛|壬癸|甲乙|丙丁", "colorRef": "木", "innerFill": 1 }, + { "content": "戊己|庚辛|壬癸|甲乙", "innerFill": 0 }, + { "content": "丙丁|戊己|庚辛|壬癸", "colorRef": "火", "innerFill": 1 }, + { "content": "子丑|寅卯|辰巳|午未", "colorRef": "金", "innerFill": 0 }, + { "content": "申酉|戌亥|子丑|寅卯", "innerFill": 1 }, + { "content": "辰巳|午未|申酉|戌亥", "colorRef": "水", "innerFill": 0 } + ] }, - + -- ======================================== - -- 第9层:8等分示例 + -- 120等分。注意:没有sectors,表示扇区没有内容,只有填色 -- ======================================== - -- 演示:规律填色 - 连续着色(无间隔) - -- 着色规律:start=1, num=2, interval=0 - -- 效果:全部扇区都着色(因为 interval=0,会连续循环) + -- 演示:规律填色 + -- 着色规律:num=3, interval=2 + -- 效果:扇区1-3着色,4、5背景色…… { - "divisions": 8, - "rInner": 430, - "rOuter": 480, - "colorRef": "强", -- 使用"强"配色(强烈红) - "innerFill": 1, -- 显示内缩 - "start": 1, -- 从第1个扇区开始 - "num": 2, -- 连续着色2个扇区 - "interval": 0 -- 0=无间隔,全部着色 - }, - - -- ======================================== - -- 第10层:12等分示例(最外层) - -- ======================================== - -- 演示:规律填色 - 1个着色,2个间隔 - -- 着色规律:start=1, num=1, interval=2 - -- 效果:扇区1着色,2-3空白,4着色,5-6空白,7着色…… - { - "divisions": 12, - "rInner": 480, - "rOuter": 500, - "colorRef": "软", -- 使用"软"配色(柔和杏) - "innerFill": 0, -- 不显示内缩 - "start": 1, -- 从第1个扇区开始 - "num": 1, -- 连续着色1个扇区 - "interval": 2 -- 着色后间隔2个扇区 + "divisions": 120, + "rInner": 300, + "rOuter": 310, + "startAngle": -4.5, -- 第一个扇区的起始角度(度,0度为正北方向,顺时针) + "innerFill": 0, -- 着色区域的内缩设置 + "colorRef": "火", -- 着色使用的颜色引用 + "num": 3, -- 连续着色3个扇区,每个区域跨3度 + "interval": 2, -- 着色后间隔1个扇区 + "groupSplit": false -- 新增:隐藏同组扇区之间的分割线, false表示不显示group中间分割线,该参数不设置,默认显示。 } ] } @@ -307,18 +263,22 @@ -- ======================================== -- 规律填色算法说明 -- ======================================== --- start: 起始扇区索引(1-based,从1开始计数) +-- startAngle: 第一个扇区的起始角度(度,0度为正北方向,顺时针增加) -- num: 连续着色扇区数量 -- interval: 着色后间隔的扇区数量 -- --- 算法:循环执行 "着色num个" → "跳过interval个" +-- 算法:从第1个扇区开始,循环执行 "着色num个" → "跳过interval个" -- 特殊情况:interval=0 表示无间隔,全部着色 -- --- 示例:divisions=12, start=2, num=3, interval=1 --- 扇区1: 空白(start之前) --- 扇区2-4: 着色(num=3) --- 扇区5: 空白(interval=1) --- 扇区6-8: 着色(num=3) --- 扇区9: 空白(interval=1) --- 扇区10-12: 着色(num=3) +-- 示例:divisions=12, startAngle=0, num=3, interval=1 +-- 扇区1-3: 着色(num=3) +-- 扇区4: 空白(interval=1) +-- 扇区5-7: 着色(num=3) +-- 扇区8: 空白(interval=1) +-- 扇区9-11: 着色(num=3) +-- 扇区12: 空白(interval=1) + +-- 示例:interval=0,表示该layer的所有扇区使用同样的colorRef +-- -- ======================================== + diff --git a/src/constants.ts b/src/constants.ts index dc6be53..40cf30a 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -24,6 +24,36 @@ export const SECTOR_INSET_DISTANCE = 1; */ export const SECTOR_STROKE_WIDTH = 0.3; +/** + * 单个扇区多文本单元布局比例预设 + * 当content使用"|"分隔多个文本时,根据单元数量自动选择布局比例 + * 1个单元:100% + * 2个单元:平均分配 [0.5, 0.5] + * 3个单元:中间大两边小 [0.25, 0.5, 0.25] + * 4个单元:中间两个大 [0.2, 0.3, 0.3, 0.2] + * 5个及以上:平均分配 + */ +export const LAYOUT_RATIO_PRESETS: Record = { + 1: [1.0], + 2: [0.5, 0.5], + 3: [0.25, 0.5, 0.25], + 4: [0.2, 0.3, 0.3, 0.2], +}; + +/** + * 获取多文本单元布局比例 + * @param unitCount 文本单元数量 + * @returns 角度分配比例数组 + */ +export const getLayoutRatio = (unitCount: number): number[] => { + if (unitCount <= 4 && LAYOUT_RATIO_PRESETS[unitCount]) { + return LAYOUT_RATIO_PRESETS[unitCount]; + } + // 5个及以上单元,平均分配 + const ratio = 1 / unitCount; + return Array(unitCount).fill(ratio); +}; + /** * 文字布局配置常量 * 用于计算扇区内文字的字体大小和位置 diff --git a/todolist.md b/todolist.md index cbf41db..422c10f 100644 --- a/todolist.md +++ b/todolist.md @@ -1,36 +1,47 @@ # 优化 ## 当前实现优化点 -1. 目前绘制罗盘代码的demo仅仅是为了测试工具类是否正确。以后统一使用json配置来实现罗盘绘制。 +1. 目前绘制罗盘代码的demo仅仅是为了测试工具类是否正确。优化为统一使用json配置来实现罗盘绘制。 2. 扇区的内容可以是是文字或者SVG图标,如为SVG,sectors的content中指定svg文件名,SVG布局规则同文字。 -3. 废弃目前颜色生成规则。使用在Json中严格配置。详见《Json文件配置》。总体规则是:优先级低到高分别是,全局背景色 - layer着色 - sectors着色。 +3. 扇区内容支持多文本单元:使用"|"分隔多个文本(如"甲乙|子|丙丁"),角度分配采用智能策略: + - 1个单元:100% + - 2个单元:平均 [0.5, 0.5] + - 3个单元:中间大 [0.25, 0.5, 0.25] + - 4个单元:中间两个大 [0.2, 0.3, 0.3, 0.2] + - 5个及以上:平均分配 + 字体大小和位置通过工具函数自动计算。单个扇区内的多文本单元之间无分割线。 +4. 废弃目前颜色生成规则。使用在Json中严格配置。详见《Json文件配置》。总体规则是:优先级低到高分别是,全局背景色 - layer着色 - sectors着色。 +5. Json中不用关注是第几层,按配置的rinner,router等参数绘制即可。 ## Json文件配置 1. 扇区背景色着色原则: 最高优先级,在layer中指定colorRef 第二优先级:colorRef规律填色,也就是说,如果同一个sector中指定了colorRef,该sector也指定了layer级别的colorRef,以前者为准,innerFill使用相同规则。 参数: - innerfill对着色区域生效 - start表示着色起始扇区 + startAngle表示第一个扇区的起始角度(以度为单位,0度为正北方向,顺时针增加) + innerfill对num、interval定义的着色扇区生效 + -- start表示着色起始扇区(已废弃,统一从第1个扇区开始) num表示连接几个单元着色 interval表示中间间隔几个单元 - 比如start=2,num=3,interval=1,意思是从第二个扇区开始着色,对2、3、4扇区着色colorref,5扇区全局背景,6、7、8着色colorref…… + 比如num=3,interval=1,意思是从第1个扇区开始着色,对1、2、3扇区着色colorref,4扇区全局背景,5、6、7着色colorref…… + groupSplit: 隐藏同组扇区之间的分割线, false表示不显示group中间分割线。如该参数不设置,取默认值true,显示。 - -- ======================================== - -- 第4层:地支 (12等分) - -- ======================================== - -- 演示:规律填色 - 3个着色,1个间隔 - -- 着色规律:start=2, num=3, interval=1 - -- 效果:扇区2-4着色,5空白,6-8着色,9空白,10-12着色,1空白 ”layers“: { + -- ======================================== + -- 12等分 + -- ======================================== + -- 演示:规律填色 - 3个着色,1个间隔 + -- 着色规律:num=3, interval=1 + -- 效果:扇区1-3着色,4背景色,5-7着色,8背景色,9-11着色,12背景色 { "divisions": 12, "rInner": 160, "rOuter": 200, + "startAngle": 0, -- 第一个扇区的起始角度(度,0度为正北方向,顺时针) "innerFill": 1, -- 着色区域的内缩设置 "colorRef": "土", -- 着色使用的颜色引用 - "start": 2, -- 从第2个扇区开始着色(1-based索引) + -- "start": 1, -- 从第1个扇区开始着色(已废弃,统一从第1个扇区开始) "num": 3, -- 连续着色3个扇区 "interval": 1, -- 着色后间隔1个扇区 "sectors": [ @@ -47,10 +58,51 @@ { "content": "戌" }, { "content": "亥", "innerFill": 0 } ] - } + }, + -- ======================================== + -- 120等分,没有sectors,表示扇区没有内容,只有填色 + -- ======================================== + -- 演示:规律填色 + -- 着色规律:num=3, interval=2 + -- 效果:扇区1-3着色,4、5背景色…… + { + "divisions": 120, + "rInner": 300, + "rOuter": 310, + "startAngle": -4.5, -- 第一个扇区的起始角度(度,0度为正北方向,顺时针) + "innerFill": 0, -- 着色区域的内缩设置 + "colorRef": "火", -- 着色使用的颜色引用 + "num": 3, -- 连续着色3个扇区,每个区域跨3度 + "interval": 2, -- 着色后间隔1个扇区 + "groupSplit": false -- 隐藏同组扇区之间的分割线, false表示不显示group中间分割线,该参数不设置,默认显示。 + }, + + -- ======================================== + -- 12等分,隐藏group分割线 + -- ======================================== + -- 演示:所有填色为colorRef,不显示group分隔线 + -- 着色规律:num=3, interval=0 + -- 效果:扇区1-3,4-6,7-9,10-12四组,只显示组间分割线,组内分割线不显示。 + { + "divisions": 12, + "rInner": 160, + "rOuter": 200, + "startAngle": 30, -- 第一个扇区的起始角度(度,0度为正北方向,顺时针) + "innerFill": 1, -- 着色区域的内缩设置 + "colorRef": "土", -- 着色使用的颜色引用 + "num": 3, -- 连续着色3个扇区 + "interval": 0, -- 着色后间隔1个扇区 + "groupSplit": false -- 隐藏同组扇区之间的分割线, false表示不显示group中间分割线,该参数不设置,默认显示。 + "sectors": [ + { "content": "子" }, { "content": "丑" }, { "content": "寅" }, + { "content": "卯" }, { "content": "辰" }, { "content": "巳" }, + { "content": "午" }, { "content": "未" }, { "content": "申" }, + { "content": "酉" }, { "content": "戌" }, { "content": "亥" } + ] + }, } -2. 中心icon配置参数 +2. 中心icon配置参数,该icon正向指北,可旋转 rIcon -- 半径 opacity -- 圆的透明度 name -- icon文件名svg,路径固定 @@ -61,7 +113,7 @@ "centerIcon": { "rIcon": 50, -- 图标半径,单位:像素 "opacity": 0.8, -- 图标透明度(0.0-1.0,0为完全透明,1为完全不透明) - "name": "taiji.svg" -- SVG图标文件名,路径固定为 /icons/ 目录 + "name": "centericon.svg" -- SVG图标文件名,路径固定为 src/assets/icons/ 目录 } 3. 360度刻度环配置参数: @@ -80,7 +132,8 @@ "showDegree": 1, -- 是否显示度数:0=不显示,1=显示(按 10° 间隔) "mode": "both", -- 刻度线模式:"inner"(在rInner外侧)、"outer"(在rOuter内侧)、"both"(两侧都有,度数居中) "opacity": 0.3, -- 圆环透明度(0.0-1.0,设置为0可以只显示刻度而不显示圆圈) - "tickLength": 6, -- 刻度线长度,单位:像素, minorTick比majorTick短1px, microTick比minorTick短1px + "tickLength": 6, -- 刻度线长度,单位:像素。 + "tickLengthStep“: 1, -- 刻度线长度差,1表示minorTick比majorTick短1px, microTick比minorTick短1px。0表示majorTick,minorTick,minorTick长度相同。 "majorTick": 10, -- 主刻度间隔(度),如 10 表示每 10° 一个主刻度 "minorTick": 5, -- 次刻度间隔(度),如 2 表示每 2° 一个次刻度 "microTick": 1, -- 微刻度间隔(度),如 1 表示每 1° 一个微刻度