Konva.js помахал обводкой вокруг элемента Rect

Если это возможно, измените обводку (границу) элемента Rect на волновой стиль, как на изображении ниже. Я позволю пользователю рисовать Rect перемещением мыши.

Я знаю, как изменить границу на Dashed, но как насчет стиля волны?

введите описание изображения здесь

Это Rect пример кода для Konva.js и документа API https://konvajs.org/api/Konva.Rect.html

<!DOCTYPE html>
<html>
  <head>
    <script src="https://unpkg.com/[email protected]/konva.min.js"></script>
    <meta charset="utf-8" />
    <title>Konva Rect Demo</title>
    <style>
      body {
        margin: 0;
        padding: 0;
        overflow: hidden;
        background-color: #f0f0f0;
      }
    </style>
  </head>
  <body>
    <div id="container"></div>
    <script>
      var width = window.innerWidth;
      var height = window.innerHeight;

      var stage = new Konva.Stage({
        container: "container",
        width: width,
        height: height,
      });

      var layer = new Konva.Layer();

      var rect1 = new Konva.Rect({
        x: 20,
        y: 20,
        width: 100,
        height: 50,
        dash: [10, 10],
        stroke: "black",
        strokeWidth: 4,
      });
      layer.add(rect1);
      stage.add(layer);
    </script>
  </body>
</html>

person MichaelMao    schedule 28.07.2020    source источник
comment
При первом чтении я подумал, что могу проголосовать против, потому что вопрос неясен и в нем нет кода «что я пробовал». Однако, если подумать, вопрос поставлен идеально. Он дает пример изображения цели и спрашивает, возможно ли это, имея в виду, что ответы могут быть аналогичными концептуальными, а затем предоставляет пример, чтобы показать путь. +1 от меня. Думаю, ответом может быть какой-то путь, но я понятия не имею, как его определить!   -  person Vanquished Wombat    schedule 28.07.2020
comment
Спасибо. Я предоставляю образец кода, чтобы люди могли его легко попробовать. Но насчет того, «что я пробую», я скажу, что изучаю только документ, а официальный сайт Konva.js также выполняет поиск по SO и ничего не может найти. О .. Я тоже спрашиваю об этом на официальном сайте Konva.js. И я нахожу только образец пунктирной границы.   -  person MichaelMao    schedule 28.07.2020
comment
@VanquishedWombat Вы говорите «какой-то путь», но если я знаю, как это написать, куда его поставить? Проблема в том, что я не могу найти, как изменить стиль границы.   -  person MichaelMao    schedule 28.07.2020
comment
Я думаю, что стиль границы фиксирован как линия, но он может иметь узор, хотя я думаю, что узор больше связан с определением штрихов или точек, которые являются «фигурным» путем в соответствии с вашим изображением. Я уверен, что @lavrton будет вместе с простым ответом вскоре, когда солнце встанет над его местоположением.   -  person Vanquished Wombat    schedule 28.07.2020
comment
Где установить выкройку? Я могу попробовать, но проблема в том, что я не знаю, где это установить.   -  person MichaelMao    schedule 28.07.2020
comment
Один из длинных подходов заключался бы в создании SVG-изображения на волнистой линии, затем добавлении прямоугольных форм «границы» к каждой стороне основного прямоугольника и повторной прорисовке SVG-изображения в каждом из них, используя соответствующую ориентацию для достижения желаемого результата. Ключевым моментом было бы нарисовать SVG так, чтобы линия казалась непрерывной.   -  person Vanquished Wombat    schedule 28.07.2020
comment
Интересный перекрывающийся вопрос здесь stackoverflow.com/questions/ 42441472 /   -  person Vanquished Wombat    schedule 28.07.2020


Ответы (1)


Итак, благодаря отличному ответу на этот аналогичный вопрос, я даю вам это. Практический подход @Paul LeBeau в этом ответе, преобразованный в Konvajs.

введите описание изображения здесь

Вот рабочий фрагмент - возможно, лучше всего просматривать в полноэкранном режиме. Также здесь, чтобы вы могли играть.

// Set up a stage
let stage = new Konva.Stage({
        container: 'container',
        width: window.innerWidth,
        height: window.innerHeight,
      }),

  // add a layer to draw on
  layer = new Konva.Layer(),

  // Add a rect to give us a path as we draw
  rubberRect = new Konva.Rect({ X:0, Y:0, width: 0, height: 0, stroke: 'transparent', visible: false}),
  
  // and a path to provide the squiggles
  rubberSquiggles  = new Konva.Path({
        x: rubberRect.position().x,
        y: rubberRect.position().y,
        data: '',
        stroke: 'cyan',
        visible: false
      });

// Stick it all into the canvas.
layer.add(rubberRect);
layer.add(rubberSquiggles);
stage.add(layer);

// Now we need a primitive state machine and position tracking so that 
// we can follow the users mouse when they are scribing a rectangle and
// clone the final rect and path when they mouseup/

// Handy object to store state and rect position drawn 
let rectPos = {state: 'waiting', x1:0, y1: 0, x2: 0, y2: 0};

// listen for mousedown
stage.on('mousedown', function(e){

  // Set state and note the mouse starting pos. 
  rectPos = {state: 'drawing', x1: e.evt.layerX , y1: e.evt.layerY, x2: e.evt.layerX, y2: e.evt.layerY};  
  
  // ANd position the rubber rect and squiggle line there.
  rubberRect.position({x: rectPos.x1, y: rectPos.y1})
  rubberSquiggles.position({x: rectPos.x1, y: rectPos.y1});
    
  // better make sure the rubber shapes are on show.
  rubberRect.show();
  rubberSquiggles.show();
    
})

// listen for mouse move
stage.on('mousemove', function(e){
  
  
  // only draw if the mouse state is 'drawing'... 
  if (rectPos.state === 'drawing'){ 
    
    // In which case note the mouse pos...
    rectPos.x2 = e.evt.layerX;
    rectPos.y2 = e.evt.layerY;   
    
    // ...and apply to the rubber rect
    rubberRect.size({
      width: rectPos.x2 - rectPos.x1,
      height: rectPos.y2 - rectPos.y1,
    });

    // and re-calc the squiggles path, then apply it direct to the shape.    
    rubberSquiggles.data(konvaSquigglePath(rubberRect, 20, 8));
    
    layer.batchDraw();
    
  }
})

// Listen for mouseup
stage.on('mouseup', function(e){
  
  // only draw if the mouse state is 'drawing'... 
  if (rectPos.state === 'drawing'){

    // switch drawing mode off
    rectPos.state = 'waiting';

    // clone the rubber shapes
    let newRect = rubberRect.clone();
    let newPath = rubberSquiggles.clone();
    layer.add(newRect)
    layer.add(newPath)

    // hide the rubber shapes until the next draw.
    rubberRect.hide();
    rubberSquiggles.hide();
  }
})
  
/* this is the func that calculates the path.
  Props to the answer by @Paul LeBeau to https://stackoverflow.com/questions/42441472/draw-a-squiggly-line-in-svg which I have shamefully adapted here to work with Konvjs.

We pass in the konva rect from which we get the simple path. The clever part is how the 
function breaks the path into small lengths and draws beziers alternating along the points.

Play with the squiggleStep and squiggleAmplitude param values to find a pleasent effect.

*/
function konvaSquigglePath(rect, squiggleStep, squiggleAmplitude){
  
  let r = {x1: 0, y1: 0, x2: rect.width(), y2: rect.height() },

      // make a simple path around the given rect - is there an easier way ? 
     p = new Konva.Path({
      data: 'M 0 0 H ' + r.x2 + ' V ' + r.y2 + ' H 0 L 0 0'
      }),

    length = p.getLength(),
    pathLen = p.getLength(),
 
    // Adjust step so that there are a whole number of steps along the path
    numSteps = Math.round(pathLen / squiggleStep),
    pos = p.getPointAtLength(0),
    newPath = "M" + [pos.x, pos.y].join(','),
    side = -1;
  
  for (let i=1; i<=numSteps; i++)
  {
    let last = pos;
    pos = p.getPointAtLength(i * pathLen / numSteps);
    
    // Find a point halfway between last and pos. Then find the point that is
    // perpendicular to that line segment, and is squiggleAmplitude away from
    // it on the side of the line designated by 'side' (-1 or +1).
    // This point will be the control point of the quadratic curve forming the
    // squiggle step.
    
    // The vector from the last point to this one
    let vector = {x: (pos.x - last.x),
                  y: (pos.y - last.y)};
    // The length of this vector
    let vectorLen = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
    // The point halfway between last point and this one
    let half = {x: (last.x + vector.x/2),
                y: (last.y + vector.y/2)};
    // The vector that is perpendicular to 'vector'
    let perpVector = {x: -(squiggleAmplitude * vector.y / vectorLen),
                      y: (squiggleAmplitude * vector.x / vectorLen)};
    // No calculate the control point position
    let controlPoint = {x: (half.x + perpVector.x * side),
                        y: (half.y + perpVector.y * side)};
    newPath += ("Q" + [controlPoint.x, controlPoint.y, pos.x, pos.y].join(','));
    // Switch the side (for next step)
    side = -side;
    
  }
  return newPath;
}
body {
  margin: 10;
  padding: 10;
  overflow: hidden;
  background-color: #f0f0f0;
}
p {
border-bottom: 1px solid #333;
}
<script src="https://unpkg.com/konva@^3/konva.min.js"></script>
<p>Draw a rectangle on the white surface.
</p>
<div id="container"></div>

person Vanquished Wombat    schedule 28.07.2020
comment
Спасибо, может быть, нет более простого способа добиться этого. Я думал об использовании Image или Star раньше - person MichaelMao; 29.07.2020
comment
Хорошая демка! Я не думаю, что есть какие-то способы использовать Konva.Rect для создания таких рисунков. Но создание пути SVG - хороший рабочий способ. - person lavrton; 29.07.2020
comment
@lavrton - спасибо. Есть ли простой способ получить путь из фигуры? Возможно, на каком-то этапе стоит добавить. shape.getPath () или аналогичный. Сэкономил бы несколько строк. - person Vanquished Wombat; 29.07.2020
comment
Вы имеете в виду от ЛЮБОЙ формы? Как по кругу? - person lavrton; 29.07.2020
comment
@lavrton да - в этом примере мне нужен был путь прямоугольника, но любой формы. - person Vanquished Wombat; 29.07.2020