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

发布于 2025-01-14 04:57:20 字数 2914 浏览 2 评论 0原文

我正在尝试使用 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);

我想要做什么:

  • 定义每个图像(或组)的可拖动区域,以免彼此之间溢出。
  • 检查图层是否可见,以便重新定位图像以隐藏图层。

I am trying to create a stage using konva.js. Basically, I need to add two (at least) or more images on the stage based on some layouts.

Example:

I have a layout that splits the stage area verically into two similar groups. Each group has one image.

Example code:

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);

What I want to do:

  • Define the draggable area of each image (or group) so to not be overflowed between each other.
  • Check if layer is visible so re-position image to hide the layer.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

匿名的好友 2025-01-21 04:57:20

因此,实现此目的的方法是使用一个矩形来定义图像将受到约束的框架。将其放入一个组中,并设置该组的剪辑区域以匹配框架矩形的位置和大小。现在将图像添加到组中。仅组中图像的一部分可见。

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

请参阅下面的代码片段(最好全屏运行)和可编辑的 CodePen 此处

当然,这只是一个图像帧,您已经描述过在您的用例中将有两个图像帧。我建议您解开代码,然后创建一个类,然后您可以根据需要使用多个类,等等。

// 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> 

有一个关于该主题的博客条目 这里

So the way to achieve this is to have a rect defining the frame in which the image will be constrained. Put this into a group and set the clip region of the group to match the position and size of the frame rect. Now add the image to the group. Only the part of the image in the group will be visible.

As a bonus, if you add a dragBoundFunc to the group you can ensure that an oversized image cannot be dragged beyond frame edges.

See snippet below (best run full-screen) and editable CodePen here.

This is, of course, only a single image frame where you have described that you will have say two in your use case. I suggest that you unravel the code then make a class, then you can use as many as required, etc.

// 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> 

There is a blog entry on the subject here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文