马黑黑 发表于 2023-11-5 13:43

改进版svg太阳花图案在线制作

<style>
.papa { margin: auto; width: 700px; display: grid; place-items: center; }
.papa svg { margin: 10px 0; width: 400px; height: 400px; }
.papa output { margin: 10px 0; max-height: 100px; overflow-y: auto; }
.set { display: flex; align-items: center; gap: 8px; }
</style>

<div class="papa">
        <p class="set">
                <svg>
                        <polygon id="poly" points="400.00 200.00, 155.50 394.99, 19.81 113.22, 324.70 43.63, 324.70 356.37, 19.81 286.78, 155.50 5.01" fill-rule="evenodd" fill="red" stroke="green" stroke-width="2" />
                </svg>
        </p>
        <p class="set">
                <label for="angRng">设置顶点数: </label>
                <input id="angRng" type="range" min="4" max="300" step="1" value="7" />
                <output id="angNum">7</output>
                <label> 【 填充模式 : </label>
                <input id="f1" type="radio" name="fill" value="" />
                <label for="f1">无</label>
                <input id="f2" type="radio" name="fill" value="nonzero" />
                <label for="f2">nonzero</label>
                <input id="f3" type="radio" name="fill" value="evenodd" checked />
                <label for="f3">evenodd 】</label>
        </p>
        <output id="pointscode">操作方法:① 拖动滑杆选择顶点数 ② 选择填充模式</output>
</div>

<script>
let addStep = (num) => {
/*连线偏移算法 :
    偶数中被 4 整除的无需偏移 +0
    奇数分两种情形:
      ① 减去 9 后被6整除的 -1
      ② 减去 15 后被 30 整除的 +1
      ③ 不符合上述两个条件的 +0
*/
        let res = 0;
        if(num % 2 === 0) {
                res = num % 4 != 0 ? -1 : 0;
        }else{
                res = (num - 9) % 6 === 0 ? -1 : 0;
                if((num - 15) % 30 === 0)res = 1;
        }
        return res;
};

/* 获取圆周上均分的点坐标 */
let getCircPoints = (num) => {
        let x = 200, y = 200, r= 200, angle = 0;
        let step = 2 * Math.PI / num, start = angle / 180 * Math.PI;
        let points = [];
        for (let j = 0; j < num; j++) {
                let a = step * j + start;
                let px = (Math.cos(a) * r + x).toFixed(2), py = (Math.sin(a) * r + y).toFixed(2);
                points.push(px + ' ' + py);
        }
        return points;
}

/* 生成 polygon 的 points 属性值 */
let mkStar = (num) => {
        let ar = getCircPoints(num), datas = [];
        let step = Math.ceil((ar.length - 3) / 2) + addStep(num);
        for(i = 0; i < ar.length; i++) {
                let idx = (i * step) % ar.length;
                datas.push(ar);
        }
        return datas.join(', ');
};

angRng.oninput = () => {
        let points = mkStar(angRng.value * 1);
        poly.setAttribute('points', points);
        pointscode.innerText = 'points="' + points + '"';
        angNum.innerText = angRng.value;
};

let fills = document.getElementsByName('fill');
fills.forEach((item,key) => {
        item.onclick = () => {
                let fillcolor = key > 0 ? 'red' : 'none';
                poly.setAttribute('fill',fillcolor);
                poly.setAttribute('fill-rule',item.value);
        };
});

</script>

马黑黑 发表于 2023-11-5 13:53

采用自己的思路,全新编写连线算法,总体实现方式更干净利落。

改版后,太阳花的顶点数理论上支持 ≥ 4 的任意顶点数,突破顶点数要被4整除的局限。一楼提供最大300个顶点数,实际上太多的顶点数连线会非常密集,到一定数值时图案会是一个圆。

不知道新算法是否经得起考验,若有问题,请及时反馈,谢谢。

马黑黑 发表于 2023-11-5 13:55

附:一楼完整代码
<style>
.papa { margin: auto; width: 700px; display: grid; place-items: center; }
.papa svg { margin: 10px 0; width: 400px; height: 400px; }
.papa output { margin: 10px 0; max-height: 100px; overflow-y: auto; }
.set { display: flex; align-items: center; gap: 8px; }
</style>

<div class="papa">
        <p class="set">
                <svg>
                        <polygon id="poly" points="400.00 200.00, 155.50 394.99, 19.81 113.22, 324.70 43.63, 324.70 356.37, 19.81 286.78, 155.50 5.01" fill-rule="evenodd" fill="red" stroke="green" stroke-width="2" />
                </svg>
        </p>
        <p class="set">
                <label for="angRng">设置顶点数: </label>
                <input id="angRng" type="range" min="4" max="300" step="1" value="7" />
                <output id="angNum">7</output>
                <label> 【 填充模式 : </label>
                <input id="f1" type="radio" name="fill" value="" />
                <label for="f1">无</label>
                <input id="f2" type="radio" name="fill" value="nonzero" />
                <label for="f2">nonzero</label>
                <input id="f3" type="radio" name="fill" value="evenodd" checked />
                <label for="f3">evenodd 】</label>
        </p>
        <output id="pointscode">操作方法:① 拖动滑杆选择顶点数 ② 选择填充模式</output>
</div>

<script>
let addStep = (num) => {
/*连线偏移算法 :
    偶数中被 4 整除的无需偏移 +0
    奇数分两种情形:
      ① 减去 9 后被6整除的 -1
      ② 减去 15 后被 30 整除的 +1
      ③ 不符合上述两个条件的 +0
*/
        let res = 0;
        if(num % 2 === 0) {
                res = num % 4 != 0 ? -1 : 0;
        }else{
                res = (num - 9) % 6 === 0 ? -1 : 0;
                if((num - 15) % 30 === 0)res = 1;
        }
        return res;
};

/* 获取圆周上均分的点坐标 */
let getCircPoints = (num) => {
        let x = 200, y = 200, r= 200, angle = 0;
        let step = 2 * Math.PI / num, start = angle / 180 * Math.PI;
        let points = [];
        for (let j = 0; j < num; j++) {
                let a = step * j + start;
                let px = (Math.cos(a) * r + x).toFixed(2), py = (Math.sin(a) * r + y).toFixed(2);
                points.push(px + ' ' + py);
        }
        return points;
}

/* 生成 polygon 的 points 属性值 */
let mkStar = (num) => {
        let ar = getCircPoints(num), datas = [];
        let step = Math.ceil((ar.length - 3) / 2) + addStep(num);
        for(i = 0; i < ar.length; i++) {
                let idx = (i * step) % ar.length;
                datas.push(ar);
        }
        return datas.join(', ');
};

angRng.oninput = () => {
        let points = mkStar(angRng.value * 1);
        poly.setAttribute('points', points);
        pointscode.innerText = 'points="' + points + '"';
        angNum.innerText = angRng.value;
};

let fills = document.getElementsByName('fill');
fills.forEach((item,key) => {
        item.onclick = () => {
                let fillcolor = key > 0 ? 'red' : 'none';
                poly.setAttribute('fill',fillcolor);
                poly.setAttribute('fill-rule',item.value);
        };
});

</script>

千羽 发表于 2023-11-5 14:43

黑黑老师又有创新了,俺不懂,等着看精彩作业{:4_187:}

马黑黑 发表于 2023-11-5 16:10

千羽 发表于 2023-11-5 14:43
黑黑老师又有创新了,俺不懂,等着看精彩作业

这个是对svg polygon标签的进一步研究,不见得一定得和做帖子有关

醉美水芙蓉 发表于 2023-11-5 16:56

马黑黑 发表于 2023-11-5 17:15

醉美水芙蓉 发表于 2023-11-5 16:56
欣赏老师新作品改进版!

这个版本,一是更换了算法,二是支持更多的顶边尖角数

红影 发表于 2023-11-5 17:22

原来连线的方式是不同的,怪不得得到的感觉不同{:4_173:}

红影 发表于 2023-11-5 17:33

连线偏移算法 :
    偶数中被 4 整除的无需偏移 +0
    奇数分两种情形:
      ① 减去 9 后被6整除的 -1
      ② 减去 15 后被 30 整除的 +1
      ③ 不符合上述两个条件的 +0

这个连线方式很复杂,先看偶数,偶数就一个情况,能被4整除或不能。
能被4整除的就按规则不偏移,那不能整除的是怎么偏移的?看了看好像使用了前一个能被4整除的方式。
再看看奇数的,看起来到一定的数值之后才能完全去遵守。

红影 发表于 2023-11-5 17:34

点的获取很简单,只是平分圆,如何连线才是关键啊{:4_187:}

红影 发表于 2023-11-5 17:39

能从4的整倍数点子的连线方式,研究出来适应所有点数的连线方式,这个太厉害了{:4_199:}

虽然对为什么区分奇偶数做这样的连线排布还没懂,但是信了黑黑说的代码跟数学有密不可分的关系,这里面一定是对应着数学规律的{:4_199:}

马黑黑 发表于 2023-11-5 17:46

红影 发表于 2023-11-5 17:22
原来连线的方式是不同的,怪不得得到的感觉不同

先前的版本是别人的算法,有四的倍数的约束。现在是自己的算法,支持不被4整除的顶点数

马黑黑 发表于 2023-11-5 17:59

红影 发表于 2023-11-5 17:33
连线偏移算法 :
    偶数中被 4 整除的无需偏移 +0
    奇数分两种情形:


圆周上点对点连线,当都是连接相邻点是,得到的是多边形;要想得到太阳花图案,需要按一定规则错开连接,这里面就涉及到算法的设计。

圆周上N个点如何去连接,总得有个开头,我们从最右边的点开始,它连接的下一个点算法是:

(N-3)/ 2

上式,向上取整。

然后从下一个点开始,继续按上述算式找下一个点,直到所有的点找完。

期间,还分支处理一些情况,凡不被 4 整除的,如果纯粹使用上面的式子,它们会重复找点,得到的结果就不是 N 个顶点,因为连线会重复。这需要做具体处理,需要一定的数学洞察力。

马黑黑 发表于 2023-11-5 18:00

红影 发表于 2023-11-5 17:34
点的获取很简单,只是平分圆,如何连线才是关键啊

重点在连线

马黑黑 发表于 2023-11-5 18:01

红影 发表于 2023-11-5 17:39
能从4的整倍数点子的连线方式,研究出来适应所有点数的连线方式,这个太厉害了

虽然对为什么区 ...

数学抽象但非常实用,它被广泛应用于各个领域。

亦是金 发表于 2023-11-5 20:19

不懂!我就是来看个热闹!{:5_102:}

马黑黑 发表于 2023-11-5 20:42

亦是金 发表于 2023-11-5 20:19
不懂!我就是来看个热闹!

{:4_191:}

小辣椒 发表于 2023-11-5 21:31

越来越精美了,黑黑脑细胞真多{:4_199:}

红影 发表于 2023-11-5 22:39

马黑黑 发表于 2023-11-5 17:46
先前的版本是别人的算法,有四的倍数的约束。现在是自己的算法,支持不被4整除的顶点数

是的,这是你自己的研究成果{:4_187:}

红影 发表于 2023-11-5 22:42

马黑黑 发表于 2023-11-5 17:59
圆周上点对点连线,当都是连接相邻点是,得到的是多边形;要想得到太阳花图案,需要按一定规则错开连接, ...

所以如何取点很有讲究{:4_187:}
页: [1] 2 3 4
查看完整版本: 改进版svg太阳花图案在线制作