马黑黑 发表于 2023-6-16 08:11

裁剪正多边形

<style>

#mydiv {
        width: 200px;
        height: 200px;
        background: gray;
}

.papa > div, .papa > p { margin: 20px 0; }

.papa label, .papa input { margin: 0 8px; }

</style>

<div class="papa">
        <p>
                <input id="btnReset" type="button" value="重置" />
                <label for="numLen">宽高:</label>
                <input id="numLen" type="number" value="200" min="100" max="1000" />
                <label for="numPoint">边数:</label>
                <input id="numPoint" type="number" value="3" min="3" max="360" />
                <input id="btnOk" type="button" value="裁剪" />
        </p>
        <div id="mydiv"></div>
        <div id="msgBox"></div>
</div>

<script>

let clipBox = (xx,points) => {
        if(points < 3) points = 3;
        let x0 = y0 = r = xx / 2, pointsAr = [];
        for(let i = 0; i < points; i ++) {
                let hudu = Math.PI / 180 * 360 / points * i;
                let x1 = x0 + Math.cos(hudu) * r, y1 = y0 + Math.sin(hudu) * r;
                pointsAr.push(x1 + 'px ' + y1 + 'px');
        }
        return `polygon(${pointsAr.join(',')})`;
}

let limit = (box,min,max) => {
        if(box.value < min) box.value = min;
        if(box.value > max) box.value = max;
}

numPoint.onchange = () => limit(numPoint,3,360);

numLen.onchange = () => limit(numLen,60,600);

btnOk.onclick = () => {
        mydiv.style.width = numLen.value + 'px';
        mydiv.style.height = numLen.value + 'px';
        mydiv.style.clipPath = clipBox(numLen.value, numPoint.value);
        msgBox.innerText = 'clip-path: ' + mydiv.style.clipPath + ';';
}

btnReset.onclick = () => {
        mydiv.style.clipPath = '';
        mydiv.style.width = '200px';
        mydiv.style.height = '200px';
        numLen.value = '200';
        numPoint.value = '3';
        msgBox.innerText = '';
}

</script>

马黑黑 发表于 2023-6-16 08:19

这是使用 clip-path: poligon() 沿元素边缘裁剪出最大化多边形的小程序,实现方式类似于割圆法,边数越多,裁剪的形状越近似圆形。代码:
<style>

#mydiv {
        width: 200px;
        height: 200px;
        background: gray;
}

.papa > div, .papa > p { margin: 20px 0; }

.papa label, .papa input { margin: 0 8px; }

</style>

<div class="papa">
        <p>
                <input id="btnReset" type="button" value="重置" />
                <label for="numLen">宽高:</label>
                <input id="numLen" type="number" value="200" min="100" max="1000" />
                <label for="numPoint">边数:</label>
                <input id="numPoint" type="number" value="3" min="3" max="360" />
                <input id="btnOk" type="button" value="裁剪" />
        </p>
        <div id="mydiv"></div>
        <div id="msgBox"></div>
</div>

<script>

let clipBox = (xx,points) => {
        if(points < 3) points = 3;
        let x0 = y0 = r = xx / 2, pointsAr = [];
        for(let i = 0; i < points; i ++) {
                let hudu = Math.PI / 180 * 360 / points * i;
                let x1 = x0 + Math.cos(hudu) * r, y1 = y0 + Math.sin(hudu) * r;
                pointsAr.push(x1 + 'px ' + y1 + 'px');
        }
        return `polygon(${pointsAr.join(',')})`;
}

let limit = (box,min,max) => {
        if(box.value < min) box.value = min;
        if(box.value > max) box.value = max;
}

numPoint.onchange = () => limit(numPoint,3,360);

numLen.onchange = () => limit(numLen,60,600);

btnOk.onclick = () => {
        mydiv.style.width = numLen.value + 'px';
        mydiv.style.height = numLen.value + 'px';
        mydiv.style.clipPath = clipBox(numLen.value, numPoint.value);
        msgBox.innerText = 'clip-path: ' + mydiv.style.clipPath + ';';
}

btnReset.onclick = () => {
        mydiv.style.clipPath = '';
        mydiv.style.width = '200px';
        mydiv.style.height = '200px';
        numLen.value = '200';
        numPoint.value = '3';
        msgBox.innerText = '';
}

</script>参与计算的圆心坐标 x0,y0,还有半径 r,在实现正多边形裁剪中,它们的值都一样,只是为了严谨性和可以体现计算依据,保留了各自的变量 名。核心问题在后续回复中会有进一步解释。

马黑黑 发表于 2023-6-16 08:20

本帖最后由 马黑黑 于 2023-6-16 14:36 编辑

本帖的核心,要解决的问题同等于:

已知圆心坐标{x0,y0}、半径r和夹角deg,求圆周上对应点的xy坐标。

圆周上任意一点连接到圆心,与通过圆心的水平线形成夹角(下称夹角,算式里用 deg 表示),这个夹角换算成弧度 hudu 后,便可通过正弦、余弦函数计算出圆周上此点的坐标{x1,y1}:

x1=x0+sin(hudu)*r
y1=y0+cos(hudu)*r

而角度转弧度的公式为: 2π/360*角度 ,简化为π/180*角度 ,则弧度:

hudu = π/180*deg

理解以上数学原理后,我们就可以在一个宽高尺寸为 ww 的矩形元素上切割出最大化的N边形(N≥3):

let x0 = y0 =r = ww / 2, pathAr = [];


for(let i = 0; i < N; i++) {
      let hudu = Math.PI / 180 * 360 / N * i;
      let x1 = x0 + sin(hudu) * r, y1 = y0 + cos(hudu) * r;
      pathAr.pushe(x1 + 'px ' + y1 + 'px');
}


let polyStr = pathAr.join(',');

上面的示范代码中:

我们首先设定了圆心坐标变量 x0 和 y0,圆的半径 r,它们都等于矩形元素宽高尺寸 ww 的一半,我们还设定了一个路径数组变量 pathAr 用以存储圆周上各个N边形的角即在圆周上的点的xy坐标值。

接着我们用一个 for 循环方法,遍历N边形的每一个角(点),并且:

① 计算出圆周上 i 点的夹角 360 / N * i 并换算成弧度 Math.PI / 180 * 360 / N * i;
② 用正、余弦函数分别计算出 i 点xy坐标值 x1 y1;
③ 将 x1 y1 以 x1px y1px 的形式存储到数组 pathAr中;

最后,将 pathAr 数组用 join 方法整合起来,形成 clip-path 裁剪路径字符串,可以 console.log(polyStr) 查看结果。

南无月 发表于 2023-6-16 12:44

想到已是不易,做到更是不简单。。。

樵歌 发表于 2023-6-16 13:53

动了前面的数字,变三角形了{:4_189:}

小辣椒 发表于 2023-6-16 13:56

好神的,我改好数字就出来代码了

马黑黑 发表于 2023-6-16 14:33

小辣椒 发表于 2023-6-16 13:56
好神的,我改好数字就出来代码了

就出裁剪路径的代码

马黑黑 发表于 2023-6-16 14:34

樵歌 发表于 2023-6-16 13:53
动了前面的数字,变三角形了

默认是三个边,可以改

马黑黑 发表于 2023-6-16 14:36

南无月 发表于 2023-6-16 12:44
想到已是不易,做到更是不简单。。。

这个仅需要具备如下知识:

一、三角函数;
二、CSS和HTML;
三、JS

梦缘 发表于 2023-6-16 16:06

问好老师,欣赏精彩分享,点赞!{:4_190:}

马黑黑 发表于 2023-6-16 17:22

梦缘 发表于 2023-6-16 16:06
问好老师,欣赏精彩分享,点赞!

{:4_190:}

樵歌 发表于 2023-6-16 17:24

马黑黑 发表于 2023-6-16 14:34
默认是三个边,可以改

弄不来,{:4_201:}

马黑黑 发表于 2023-6-16 17:24

樵歌 发表于 2023-6-16 17:24
弄不来,

就是边数,你改为4,5,20,36看看

南无月 发表于 2023-6-16 17:57

马黑黑 发表于 2023-6-16 14:36
这个仅需要具备如下知识:

一、三角函数;


反正只有黑师整得出来。我溜边儿用用就很不错了{:4_173:}

马黑黑 发表于 2023-6-16 18:53

南无月 发表于 2023-6-16 17:57
反正只有黑师整得出来。我溜边儿用用就很不错了

数学不是体育老师教的一般都木有什么问题

南无月 发表于 2023-6-16 20:10

马黑黑 发表于 2023-6-16 18:53
数学不是体育老师教的一般都木有什么问题

{:4_170:}有问题也木有关系,一楼点点就有了

小辣椒 发表于 2023-6-16 20:35

马黑黑 发表于 2023-6-16 14:33
就出裁剪路径的代码

这个确实很神,我电脑里面还做不出来,晕的,果然数学是体育老师教的

马黑黑 发表于 2023-6-16 20:38

小辣椒 发表于 2023-6-16 20:35
这个确实很神,我电脑里面还做不出来,晕的,果然数学是体育老师教的

不会的?女生拿把剪刀胡剪一通不是个事吧

马黑黑 发表于 2023-6-16 20:39

南无月 发表于 2023-6-16 20:10
有问题也木有关系,一楼点点就有了

这个,也许,可能,大概会不是自己所需要的呢?还是得学会自己动手剪一剪

马黑黑 发表于 2023-6-16 21:06

小辣椒 发表于 2023-6-16 20:35
这个确实很神,我电脑里面还做不出来,晕的,果然数学是体育老师教的

随便做一个div,有背景色,然后加入 clip-path 即可:

<style>
.box {
        width: 200px;
        height: 200px;
        background: purple;
        clip-path: polygon(200px 100px, 130.902px 195.106px, 19.0983px 158.779px, 19.0983px 41.2215px, 130.902px 4.89435px);
}
</style>

<div class="box"></div>
页: [1] 2 3 4
查看完整版本: 裁剪正多边形