如何在 Konva js 舞台上处理图像(溢出、可拖动区域等)?

How to handle with images (overflow, draggable area, etc) on Konva js stage?

我正在尝试使用 konva.js 创建一个舞台。基本上,我需要根据一些布局在舞台上添加两个(至少)或更多图像。

示例:

我有一个布局,将舞台区域真实地分成两个相似的组。每组一张图片。

示例代码:

var stage = new Konva.Stage({
              container: 'container',
              width: width, // 295px
              height: height, // 600px
});

var layer = new Konva.Layer({
              imageSmoothingEnabled: true
});

// add a vertical line in order to show seperated groups
var line = new Konva.Line({
              points: [stage.width() / 2, 0, stage.width() / 2, stage.height()],
              stroke: '#9499a3',
              strokeWidth: 2,
              lineCap: 'round',
              lineJoin: 'round',
});
layer.add(line)

// create group #1
var group1 = new Konva.Group({
             x: 0,
             y: 0,
             width: stage.width() / 2,
             height: stage.height()
});

var image1;
var imageObj = new Image();
imageObj.onload = function () {
                    
                    image1 = new Konva.Image({
                        x: (stage.width() / 2 - imageObj.width) - line.strokeWidth() / 2,
                        //y: 0,
                        width: imageObj.width,
                        height: stage.height(),
                        image: imageObj,
                        draggable: true,
                    });

                    //layer.add(image1);
                    group1.add(image1);

                    image1.on('dragstart', function () {
                        console.log('dragstart')
                    });

                    image1.on('dragmove', function(e){
                        console.log('X : ' + this.attrs.x + ', Y : ' + this.attrs.y)
                    });

                    image1.on('dragend', function () {
                        console.log('X : ' + this.attrs.x + ', Y : ' + this.attrs.y)
                    });
};
imageObj.src = 'img/1.jpg';

// create group #2
var group2 = new Konva.Group({
                x: stage.width() / 2  + line.strokeWidth() / 2,
                y: 0,
                width: stage.width() / 2,
                height: stage.height()
});

var image2;
var imageObj = new Image();
imageObj.onload = function () {

                    image2 = new Konva.Image({
                        //x: stage.width() / 2  + line.strokeWidth() / 2,
                        //y: stage.height() / 2 - imageObj.height / 2,
                        width: imageObj.width,
                        height: stage.height(),
                        image: imageObj,
                        draggable: true,
                    });
                    
                    //layer.add(image2);
                    group2.add(image2);
};
imageObj.src = 'img/2.jpg';

layer.add(group1, group2)
stage.add(layer);

我想做的事情:

所以实现这一点的方法是用一个矩形定义图像将被约束的框架。将其放入一个组中,并设置该组的剪辑区域以匹配框架矩形的位置和大小。现在将图像添加到组中。只有组中的图像部分可见。

作为奖励,如果您将 dragBoundFunc 添加到组中,您可以确保不会将超大图像拖到框架边缘之外。

请参阅下面的代码片段(最佳 运行 full-screen)和可编辑的 CodePen here

当然,这只是一个图像帧,您在其中描述了在您的用例中会说两个图像帧。我建议你解开代码然后制作一个class,然后你可以使用多少个等等

// this data gives the position and size of the frame
let data = {
  frameGroup: { x: 50, y: 100, width: 800, height: 300, strokeWidth: 10, stroke: 'cyan'},
  fadeImage: {opacity: 0.3}
}

// add a stage
let stage = new Konva.Stage({container: 'container', width: 1000, height: 500 }),
    layer = new Konva.Layer({}), // Add a layer and group to draw on
    group = new Konva.Group({clip: data.frameGroup}),
    rect = new Konva.Rect(data.frameGroup),
    image = new Konva.Image({draggable: true}),
    fadeImage = null,
    imageObj = new Image();

stage.add(layer);
layer.add(group)
group.add(rect);

rect.listening(false); // stop the frame rect intercepting events 



// Use the html image object to load the image and handle when laoded. 
imageObj.onload=function () {

  image.image(imageObj); // set the Konva image content to the html image content
  
  // compute image position so that it is initially centered in the frame
  let imagePos = getMiddlePos(data.frameGroup, data.frameGroup, {width: imageObj.width, height: imageObj.height}); 
  
  // set the Konva image attributes as needed
  image.setAttrs({
    x: imagePos.x, y: imagePos.y, width: imageObj.width, height: imageObj.height,
    
    // This function ensures the oversized image cannot be dragged beyond frame edges.
    // Is is firect by the drag event.
    dragBoundFunc: function(pos){
      var imagePos = this.getClientRect(); // get the image dimensions.
      let 
          maxPos = { // compute max x & y position allowed for image
            x: data.frameGroup.x,
            y: data.frameGroup.y
          },
          minPos = {
            x: data.frameGroup.x + data.frameGroup.width - imagePos.width,
            y: data.frameGroup.y + data.frameGroup.height - imagePos.height
          },
      
          newX = (pos.x >= maxPos.x) ? maxPos.x : pos.x, // ensure left edge not within frame
          newY = (pos.y >= maxPos.y) ? maxPos.y : pos.y; // ensure top edge not within frame
      
      newX = newX < minPos.x ? minPos.x : newX; // ensure right edge not within frame
      newY = newY < minPos.y ? minPos.y : newY; // ensure top edge not within frame
      
      fadeImage.setAttrs({x: newX, y: newY}); // apply what we computed

      // dragBoundFunc must return a value with x & y. Either return same value passed in
      // or modify the value.
      return {
        x: newX,
        y: newY
      };
    }
  })
 
  group.add(image) // add the image to the frame group
  image.moveToBottom(); // ensure the frame rect is above the image in the z-index.

  // make a clone of the image to be used as the fade image.
  fadeImage = image.clone({draggable: false, opacity: data.fadeImage.opacity});
  layer.add(fadeImage);  
  // ensure fade image is one step below the frame group. ! - in this simple demo Konva will raise 
  // a warning because group.zIndex() = 0. 
  fadeImage.zIndex(group.zIndex() - 1); 
}
imageObj.src = "https://assets.codepen.io/255591/hubble_space_image.jpg?x=1"


// simple function to get the x & y for the image to be centered in the frame
function getMiddlePos(framePos, frameSize, imageSize){
  return{
    x: framePos.x + (frameSize.width - imageSize.width)/2,
    y: framePos.y + (frameSize.height - imageSize.height)/2,
  }
}


// Toggle use of fade image to show overflowed part of image.
$('#useFadImage').on('change', function(e){
  
  if (fadeImage){
    fadeImage.visible(!fadeImage.visible());
  }
})
body {
  margin: 14px;
  padding: 10px;
  font: 12pt Verdana, Arial, sans-serif;
}
#container {
  width: 1000px;
  height: 500px;
  border: 1px solid red;
} 
<p><h2>Constrain an image to the bounds of a frame</h2></p>
<p>1. Set the frame group clip region.</p>
<p>2. Set dragBounds function on the image so that it cannot escape the group.</p>
<p>Drag the image and note that it cannot me dragged such that white space in the frame would be visible.</p>
<p>
  <input type='checkbox' id='useFadImage' checked='' /><label for="useFadImage">Use fade image - shows area of image outside the frame </label>
</p>
<div id='container'  ></div>
 <p>Image from <a href="https://www.nasa.gov/multimedia/imagegallery/iotd.html">NASA Image of the day.</a></p>
 <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js'></script>
<script src="https://unpkg.com/konva@8/konva.min.js"></script> 

有一篇关于主题 here 的博客条目。