RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1257392
Accepted
Floyat
Floyat
Asked:2022-03-18 06:29:29 +0000 UTC2022-03-18 06:29:29 +0000 UTC 2022-03-18 06:29:29 +0000 UTC

仅相对于中心线笔划向内更改 svg 形状笔划的粗细。第2部分

  • 772

https://jsfiddle.net/ru8fzeya/5/

//polygon
function changeStrokePoly() {
   var poly = document.getElementById("poly"),
         isw  = document.getElementById("isw-poly"); 
   poly.setAttribute("stroke-width",isw.value);
};

function changeSizePoly(){
   var poly     = document.getElementById("poly"),
       polyMask = document.getElementById("msk-poly"),
       isz      = document.getElementById("isz-poly"); 
 
   var _getCoordinates = function(scale){
     var viewBox   = 400,
         placeSize = 380 * (scale / 100),
                 h         = placeSize * (Math.sqrt(3) / 2);

            var offsetX = (viewBox - placeSize) / 2,
                offsetY = (viewBox - h) / 2,
                coordinates = [
                    {
                        x : (placeSize / 2 + offsetX),
                        y : offsetY
                    },
                    {
                        x : (offsetX),
                        y : (h + offsetY),
                    },
                    {
                        x : (placeSize + offsetX),
                        y : (h + offsetY)
                    }
                ];
        
        return coordinates;
   }
   
   var coordinates = _getCoordinates(isz.value),
         coordinatesString = coordinates[0].x + ' ' + coordinates[0].y + ' ' + coordinates[1].x + ' ' + coordinates[1].y + ' ' + coordinates[2].x + ' ' + coordinates[2].y;
   
   poly.setAttribute('points', coordinatesString);
   polyMask.setAttribute('points', coordinatesString); 
};

//circle
function changeStrokeCircle() {
   var circle = document.getElementById("circle"),
         isw  = document.getElementById("isw-circle"); 
   circle.setAttribute("stroke-width",isw.value);
};

function changeSizeCircle(){
   var circle     = document.getElementById("circle"),
       circleMask = document.getElementById("msk-circle"),
       isz        = document.getElementById("isz-circle"); 
 
   circle.setAttribute('r', isz.value);
   circleMask.setAttribute('r', isz.value);
};

//rect
function changeStrokeRect() {
   var rect = document.getElementById("rect"),
         isw  = document.getElementById("isw-rect"); 
   rect.setAttribute("stroke-width",isw.value);
};

function changeSizeRect(){
   var rect      = document.getElementById("rect"),
       rectMask  = document.getElementById("msk-rect"),
       isz       = document.getElementById("isz-rect"),
       value     = isz.value,
       maxWidth  = 300,
       maxHeight = 100,
       width     = maxWidth * value / 100,
       height    = maxHeight * value / 100;
  
   rect.setAttribute('width', width);
   rect.setAttribute('height', height);
   
   rectMask.setAttribute('width', width);
   rectMask.setAttribute('height', height);
};
<div>
 <div>
 Polygon--
 <input id="isw-poly" type="range" min="1" value="30" max="219"  oninput="changeStrokePoly()"/>
 <input id="isz-poly" type="range" min="10" value="100" max="100"  oninput="changeSizePoly()"/>
 </div>
 <div>
 Circle-----
 <input id="isw-circle" type="range" min="1" value="30" max="360"  oninput="changeStrokeCircle()"/>
 <input id="isz-circle" type="range" min="10" value="180" max="180"  oninput="changeSizeCircle()"/>
 </div>
 
 <div>
  Rect-------
  <input id="isw-rect" type="range" min="1" value="30" max="100"  oninput="changeStrokeRect()"/>
  <input id="isz-rect" type="range" min="10" value="100" max="100"  oninput="changeSizeRect()"/>
 </div>
</div>

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="500" height="400" viewBox="0 0 800 400" >
<defs>
<mask id="msk1">
<polygon id="msk-poly" fill="#fff" points="200 35.5 10 364.5 390 364.5"></polygon>
</mask> 
<mask id="msk2">
<circle id="msk-circle" fill="#fff" cx="600" cy="200" r="180"></circle>
</mask>
<mask id="msk3">
<rect id="msk-rect" fill="#fff" x="200" y="-100" width="300" height="100"></rect>
</mask> 
</defs> 
    <polygon id="poly" mask="url(#msk1)" fill="none" stroke-width="30" stroke="red" points="200 35.5 10 364.5 390 364.5"></polygon>
    <circle id="circle" mask="url(#msk2)" fill="none" stroke-width="30" stroke="green" cx="600" cy="200" r="180"></circle>
    <rect id="rect" mask="url(#msk3)" fill="none" stroke-width="30" stroke="blue" x="200" y="-100" width="300" height="100"></rect>
</svg>

有 3 种形状:等边三角形、圆形和矩形。还有用于更改笔画粗细和调整形状大小的滑块。这些数字是相互独立的。正如您在示例中看到的,形状的轮廓仅向内变化。由于遮罩元素和@Alexandr_TT帮助我解决了这个问题,以及这个问题的替代解决方案,外部笔划被剪掉了。

但是由于 使用蒙版/剪辑路径并非一切都是完美的(在此处阅读有关该问题的更多信息),然后我正在寻找一种在方法方面可能更复杂但更通用的解决方案,该解决方案将基于数学和几何。

我希望能够分别使用一个多边形、一个矩形和一个圆形来解决每个形状的问题。没有蒙版,没有剪辑路径和其他外部元素。也就是只需要重新计算三角形的坐标,矩形的宽高和圆的半径。

附加限制:

  1. 三角形的坐标、初始半径或圆心的坐标,以及矩形的宽度和高度都是任意的。在示例中,它们的含义只是一个示例。三角形总是等边的(为简单起见)。
  2. 您不能更改 viewBox、使用 css、蒙版和剪辑路径。
  3. 在任何情况下,笔划都不会超出形状的边界。
  4. 描边可以完全“填充”形状内部的背景。
  5. 使用滑块调整形状的笔触和大小。
  6. SVG 本身可以有许多不同的其他形状和元素,这个决定不应该影响它们。

问题:如何做到这一点?

这是一个示例,其中所有不必要的内容都已删除:https ://jsfiddle.net/ru8fzeya/6/ 只剩下形状和更改笔触粗细/大小。但是 stroke 的默认行为是在两个方向上扩展,您需要获得与示例中使用遮罩但不使用遮罩相同的结果。

javascript
  • 2 2 个回答
  • 10 Views

2 个回答

  • Voted
  1. Alexandr_TT
    2022-03-18T22:43:43Z2022-03-18T22:43:43Z

    而且因为 有了面具,并非一切都是完美的(这个问题写在这个问题上,带面具的解决方案在这里),那么问题是 - 如何达到与示例中相同的效果,但没有面具?

    让我们尝试用clipPath

    使用了问题中的代码。将遮罩更改为 clipPath。剪裁原理与遮罩相同 - 剪裁定义中指定的形状之外的所有内容都将被剪裁。将显示内部内容。因此,字符串只会向内增长。

    添加了坐标网格以可视化更改。

    function changeStroke() {
       var poly = document.getElementById("poly"),
             isw  = document.getElementById("isw"); 
       poly.setAttribute("stroke-width",isw.value);
    };
    
    function changeSize(){
       var poly     = document.getElementById("poly"),
           polyMask = document.getElementById("msk-poly"),
           isz      = document.getElementById("isz"); 
     
       var _getCoordinates = function(scale){
         var viewBox   = 400,
             placeSize = 380 * (scale / 100),
                     h         = placeSize * (Math.sqrt(3) / 2);
    
                var offsetX = (viewBox - placeSize) / 2,
                    offsetY = (viewBox - h) / 2,
                    coordinates = [
                        {
                            x : (placeSize / 2 + offsetX),
                            y : offsetY
                        },
                        {
                            x : (offsetX),
                            y : (h + offsetY),
                        },
                        {
                            x : (placeSize + offsetX),
                            y : (h + offsetY)
                        }
                    ];
            
            return coordinates;
       }
       
       var coordinates = _getCoordinates(isz.value),
             coordinatesString = coordinates[0].x + ' ' + coordinates[0].y + ' ' + coordinates[1].x + ' ' + coordinates[1].y + ' ' + coordinates[2].x + ' ' + coordinates[2].y;
       
       poly.setAttribute('points', coordinatesString);
       polyMask.setAttribute('points', coordinatesString);
       
    };
    <div> <input id="isw" type="range" min="1" value="4" max="219"  oninput="changeStroke()"/>
         <input id="isz" type="range" min="10" value="100" max="100"  oninput="changeSize()"/> </div>
        
        <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1000" height="1000" viewBox="-20 -20 1050 1050" preserveAspectRatio="xMinYMin meet" >
        <defs>
        <clipPath id="msk">
        <polygon id="msk-poly" fill="#fff"  points="200 35.5 10 364.5 390 364.5"></polygon>
        </clipPath>   
        
        <pattern id="p10" width="10" height="10" patternUnits="userSpaceOnUse">
            <path d="M 10 0 L 0 0 0 10" fill="none" stroke="gray" stroke-width="0.5"/>
          </pattern>
          <pattern id="p100" width="100" height="100" patternUnits="userSpaceOnUse">
            <rect width="100" height="100" fill="url(#p10)"/>
            <path d="M 100 0 L 0 0 0 100" fill="none" stroke="gray" stroke-width="1"/>
          </pattern>
        </defs>  
        <rect width="1000" height="1000" fill="url(#p100)" /> 
          <!-- Вертикальный блок цифр -->
    
          <text x="-15" y="0">0,0</text> 
           <text x="-20" y="100" >100</text> 
            <text x="-20" y="200">200</text> 
             <text x="-20" y="300">300</text> 
              <text x="-20" y="400">400</text> 
               <text x="-20" y="500">500</text> 
              <text x="-20" y="600">600</text> 
             <text x="-20" y="700">700</text> 
            <text x="-20" y="800">800</text>
           <text x="-20" y="900">900</text> 
          <text x="-20" y="1000">1000</text> 
              <!-- Горизонтальный блок --> 
         <text x="100" y="-5" >100</text> 
           <text x="200" y="-5" >200</text> 
            <text x="300" y="-5">300</text> 
             <text x="400" y="-5">400</text> 
              <text x="500" y="-5">500</text>    
             <text x="600" y="-5">600</text> 
             <text x="700" y="-5">700</text> 
            <text x="800" y="-5">800</text>
           <text x="900" y="-5">900</text>  
          <text x="990" y="-5">1000</text>   
          
            <polygon id="poly" clip-path="url(#msk)" fill="none" stroke-width="4" stroke="red"  points="200 35.5 10 364.5 390 364.5"></polygon>
        </svg>

    • 6
  2. Best Answer
    Daniil Loban
    2022-03-20T14:42:37Z2022-03-20T14:42:37Z

    该解决方案基于找到中心并计算偏移量的原理,尽管对于每个给定的数字,它的计算方式略有不同。

    图形表示

    所以,比如有一种形状,我们想增加1个像素的笔画粗细,自然会增加видимый размер形状,这正是需要补偿(减小形状)的地方,如图所示以上。

    由于需要计算补偿,将计算分成两个函数来计算线的大小和粗细是没有意义的。

    对于圆,最简单的是:半径增加 1 个像素会导致直径增加 2 个像素:

    • 半径重新计算公式радиус - толщина_обводки / 2;

    对于一个 rectangle ,它有点复杂:

    • x位置公式: начальная_позиция_x + толщина_обводки / 2
    • y位置公式: начальная_позиция_x + толщина_обводки / 2
    • 宽度公式:ширина - толщина_обводки
    • 身高公式:высота - толщина_обводки

    对于多边形来说,这更加困难:

    • 你需要得到所有的点(例如,从 html 标记)
    • 根据主要思想,找到центр点会发散/收敛的地方
    • 此外,我们发现расстояние от центра和угол в градусах对于每个点
    • 计算 x 的新坐标

    центр + (расстояние_от_центра - толшина_обводки + размер) * косинус(угла)

    • 计算 y 的新坐标

    центр + (расстояние_от_центра - толшина_обводки + размер) * синус(угла)

    下面是一个解决方案,代码不是很优化,但是我们看了下,唯一没解决的一点就是如果线粗大于半径,那么就重叠svg begin的特性,然后图形就被渲染或者“绘制”了,所以先最好设置笔画粗细为1像素的大小,然后加上粗细,一般情况下,我觉得根据图的大小加个取值范围的修正并不难so没有这样的覆盖。

        //polygon
        const getPolyPoints = ( id ) => {
          var poly = document.getElementById( id );
          const points = poly.getAttribute( 'points' ).split(' ');
          const result = []
          for ( let i = 0; i < points.length; i += 2 ){
            result.push({
              x: Number( points[i] ),
              y: Number( points[i + 1] )
            })
          }  
          return result;
        }
    
        // находим усредненный центр всех точек
        const getPolyCenter = ( points ) => {
          let x = 0,
              y = 0;
          for( let i = 0 ; i < points.length; i++ ){
            x += points[i].x;
            y += points[i].y;
          }
          x /= points.length;
          y /= points.length;
          return({ x, y });
        }
    
        const setPointsInfo = ( points, center ) => {
          for(let i = 0 ; i < points.length; i++){
            // находим растояние из центра 
            points[i].r = 
              Math.sqrt( Math.pow(Math.abs(points[i].x - center.x), 2) 
                      +  Math.pow(Math.abs(points[i].y - center.y), 2)
              )
            // находим угол в градусах
            // из центра до точки
            points[i].a = 
              Math.atan2(
                points[i].y - center.y,
                points[i].x - center.x
              ) / ( Math.PI/180 );
          } 
        } 
    
        const points = getPolyPoints("poly");
        const center = getPolyCenter(points);
        setPointsInfo(points, center)
    
        function changePoly() {
          var poly = document.getElementById("poly"),
              isw  = document.getElementById("isw-poly"), 
              isz  = document.getElementById("isz-poly"); 
          // высчитываем новые координаты (можно добавить поворот)
          let pts = "";
          for(let i = 0; i < points.length; i++){
            const x = center.x + (points[i].r - isw.value + Number(isz.value)) 
              * Math.cos(points[i].a  * ( Math.PI/180 ))
            const y = center.y + (points[i].r - isw.value + Number(isz.value)) 
              * Math.sin(points[i].a  * ( Math.PI/180 ))
            pts += `${x} ${y} ` 
          } 
          poly.setAttribute("stroke-width",isw.value);
          poly.setAttribute('points', pts); 
        };
    
        //circle
        // не нужно дополнительных переменных поскольку
        // координаты считаются от центра
        function changeCircle() {
           var circle = document.getElementById("circle"),
               isw  = document.getElementById("isw-circle"),
               isz  = document.getElementById("isz-circle"); 
           circle.setAttribute("stroke-width",isw.value);
           // рассчет смещения
           circle.setAttribute('r', isz.value - isw.value/2);
        };
    
        //rect
        // дополнительные преременные для правильного смещения  
        const rectOrigX =  +document.getElementById("rect").getAttribute("x");
        const rectOrigY =  +document.getElementById("rect").getAttribute("y");
    
        function changeRect() {
           var rect = document.getElementById("rect"),
               isw  = document.getElementById("isw-rect"),
               isz       = document.getElementById("isz-rect"),
               maxWidth  = 300,
               maxHeight = 100,
               width     = maxWidth * isz.value / 100,
               height    = maxHeight * isz.value / 100;
           
           rect.setAttribute( "stroke-width", isw.value );
           // рассчет смещения
           rect.setAttribute( "x", rectOrigX + Number(isw.value) / 2 );
           rect.setAttribute( "y", rectOrigY + Number(isw.value) / 2 );
           rect.setAttribute( "width", width -  isw.value );
           rect.setAttribute( "height", height -  isw.value );
        };
    
       // корректируем изначальную отрисовку
       // в идеале лучше создать классы для фигур
       // которые будут их пересчитывать
       changePoly();
       changeCircle();
       changeRect();
    <!DOCTYPE html>
    <html lang="ru">
    <head>
      <meta charset="utf-8">
      <title></title>
    </head>
    <body>
      <div>
        <div>
          Polygon
          <input id="isw-poly" type="range" min="1" value="15" max="219"  oninput="changePoly()"/>
          <input id="isz-poly" type="range" min="10" value="10" max="100"  oninput="changePoly()"/>
        </div>
        <div>
          Circle
          <input id="isw-circle" type="range" min="1" value="15" max="360"  oninput="changeCircle()"/>
          <input id="isz-circle" type="range" min="10" value="180" max="180"  oninput="changeCircle()"/>
        </div>
        <div>
          Rect
          <input id="isw-rect" type="range" min="1" value="15" max="100"  oninput="changeRect()"/>
          <input id="isz-rect" type="range" min="10" value="100" max="100"  oninput="changeRect()"/>
        </div>
      </div>
      
      <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="500" height="400" viewBox="0 0 800 400" >
        <polygon id="poly" fill="none" stroke-width="15" stroke="red" points="200 35.5 10 364.5 390 364.5"></polygon>
        <circle id="circle" fill="none" stroke-width="15" stroke="green" cx="600" cy="200" r="180"></circle>
        <rect id="rect"  fill="none" stroke-width="15" stroke="blue" x="200" y="-100" width="300" height="100"></rect>
      </svg>
    </body>
    </html>

    • 4

相关问题

  • 第二个 Instagram 按钮的 CSS 属性

  • 由于模糊,内容不可见

  • 弹出队列。消息显示不正确

  • 是否可以在 for 循环中插入提示?

  • 如何将 JSON 请求中的信息输出到数据表 Vuetify vue.js?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    表格填充不起作用

    • 2 个回答
  • Marko Smith

    提示 50/50,有两个,其中一个是正确的

    • 1 个回答
  • Marko Smith

    在 PyQt5 中停止进程

    • 1 个回答
  • Marko Smith

    我的脚本不工作

    • 1 个回答
  • Marko Smith

    在文本文件中写入和读取列表

    • 2 个回答
  • Marko Smith

    如何像屏幕截图中那样并排排列这些块?

    • 1 个回答
  • Marko Smith

    确定文本文件中每一行的字符数

    • 2 个回答
  • Marko Smith

    将接口对象传递给 JAVA 构造函数

    • 1 个回答
  • Marko Smith

    正确更新数据库中的数据

    • 1 个回答
  • Marko Smith

    Python解析不是css

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5