添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
private const double ScaleInternal = 0.2; protected override void OnMouseWheel(MouseWheelEventArgs e) base.OnMouseWheel(e); ScaleImg(e.Delta > 0); private void ScaleImg(bool isEnlarge) //获取放大/缩小系数 var tempScale = isEnlarge ? ImageScale + ScaleInternal : ImageScale - ScaleInternal; //最小允许缩小到缩放比间隔,最大允许放大到50倍 if (Abs(tempScale) < ScaleInternal || Abs(tempScale - 50) < 0.001) return; ImageScale = tempScale;

但这个缩放存在一个问题,缩放中心在 (0,0) ,这就意味着图像左上角不动,往右、下边拉伸,这通常与用户意愿不符,合理的缩放应该是用户将鼠标移到关心的位置,缩放时焦点应该一直保持在这个位置,以便用户观察细节

图片往右、下拉伸

要实现缩放时焦点保持在鼠标位置,那就需要移动图像来配合,在做这个功能之前,先实现一个最简单的效果——居中缩放,图像在未铺满容器前能看到所有内容,这时候不允许移动,因此在可移动前先居中缩放至铺满容器

改造一下 ScaleImg 函数:

private void ScaleImg(bool isEnlarge)
    //获取放大/缩小系数
    var tempScale = isEnlarge ? ImageScale + ScaleInternal : ImageScale - ScaleInternal;
    //最小允许缩小到缩放比间隔,最大允许放大到50倍
    if (Abs(tempScale) < ScaleInternal || Abs(tempScale - 50) < 0.001)
        return;
    ImageScale = tempScale;
    var baseMarginX = .5 * _scaleInternalWidth;
    var baseMarginY = .5 * _scaleInternalHeight;
    //默认偏移都只取缩放间隔的一半,以便居中
    var marginX = baseMarginX;
    var marginY = baseMarginY;
    ImageMargin = isEnlarge ? new Thickness(ImageMargin.Left - marginX, ImageMargin.Top - marginY, 0, 0) :
        new Thickness(ImageMargin.Left + marginX, ImageMargin.Top + marginY, 0, 0); ;

这里在缩放后,ImageMargin的Top、Left分别 +/- 上缩放间隔宽高的一半就实现了居中,宽高的缩放间隔在得到图像后就不再变化了,因此放在初始化的时候计算

/// <summary>
///     缩放高度间隔
/// </summary>
private double _scaleInternalHeight;
/// <summary>
///     缩放宽度间隔
/// </summary>
private double _scaleInternalWidth;
private void Init()
    if (ImageSource == null || !_isLoaded) return;
    ImageScale = 1;
    //记录宽高
    ImageWidth = ImageSource.PixelWidth;
    ImageHeight = ImageSource.PixelHeight;
    //原始宽高
    ImageOriWidth = ImageSource.PixelWidth;
    ImageOriHeight = ImageSource.PixelHeight;
    //记录当前缩放间隔下的缩放宽高
    _scaleInternalWidth = ImageOriWidth * ScaleInternal;
    _scaleInternalHeight = ImageOriHeight * ScaleInternal;
    //记录图像宽高比
    _imgWidHeiScale = _imgWidHeiScale = ImageWidth / ImageHeight;
    ImgSizeAdaption();
    AutoCentering();

改动后效果如下:

缩放中心从(0,0)到了图像中心

下一步,图片铺满容器后缩放中心移到鼠标指针的位置

再改造一下ScaleImg函数,当图像铺满容器后让ImageMargin以鼠标位置在图像长宽上的比例移动,这时候缩放中心就到了鼠标指针位置了

private void ScaleImg(bool isEnlarge)
    var beforeScaleImgWidth = ImageWidth;
    var beforeScaleImgHeight = ImageHeight;
    //获取放大/缩小系数
    var tempScale = isEnlarge ? ImageScale + ScaleInternal : ImageScale - ScaleInternal;
    //最小允许缩小到缩放比间隔,最大允许放大到50倍
    if (Abs(tempScale) < ScaleInternal || Abs(tempScale - 50) < 0.001)
        return;
    ImageScale = tempScale;
    //获取鼠标在画布上的位置
    var posCanvas = Mouse.GetPosition(_imgPanel);
    //对应在图像上的坐标
    var posImg = new Point(posCanvas.X - ImageMargin.Left, posCanvas.Y - ImageMargin.Top);
    var baseMarginX = .5 * _scaleInternalWidth;
    var baseMarginY = .5 * _scaleInternalHeight;
    //默认偏移都只取缩放间隔的一半,以便居中
    var marginX = baseMarginX;
    var marginY = baseMarginY;
    //图片尺寸未大于容器尺寸前以图片中点作为缩放中心,图像尺寸大于容器尺寸时以鼠标位置作为缩放中心
    if (beforeScaleImgWidth >= ActualWidth && beforeScaleImgHeight >= ActualHeight)
        //按鼠标位置与图片长度的比例调整xy移动
        marginX = posImg.X / beforeScaleImgWidth * _scaleInternalWidth;
        marginY = posImg.Y / beforeScaleImgHeight * _scaleInternalHeight;
    ImageMargin = isEnlarge ? new Thickness(ImageMargin.Left - marginX, ImageMargin.Top - marginY, 0, 0) :
        new Thickness(ImageMargin.Left + marginX, ImageMargin.Top + marginY, 0, 0); ;
鼠标停留在红色像素的格子上放大

这时候又引入了一个新问题,在不同点缩放后图像不再居中了,更严重的时候图像甚至会移动到容器之外

不同点缩放

再做一点改动来解决这个问题,加上两个策略

//获取放大/缩小系数 var tempScale = isEnlarge ? ImageScale + ScaleInternal : ImageScale - ScaleInternal; //最小允许缩小到缩放比间隔,最大允许放大到50倍 if (Abs(tempScale) < ScaleInternal || Abs(tempScale - 50) < 0.001) return; ImageScale = tempScale; //获取鼠标在画布上的位置 var posCanvas = Mouse.GetPosition(_imgPanel); //对应在图像上的坐标 var posImg = new Point(posCanvas.X - ImageMargin.Left, posCanvas.Y - ImageMargin.Top); var baseMarginX = .5 * _scaleInternalWidth; var baseMarginY = .5 * _scaleInternalHeight; //默认偏移都只取缩放间隔的一半,以便居中 var marginX = baseMarginX; var marginY = baseMarginY; //图片尺寸未大于容器尺寸前以图片中点作为缩放中心,图像尺寸大于容器尺寸时以鼠标位置作为缩放中心 if (beforeScaleImgWidth >= ActualWidth && beforeScaleImgHeight >= ActualHeight) //按鼠标位置与图片长度的比例调整xy移动 marginX = posImg.X / beforeScaleImgWidth * _scaleInternalWidth; marginY = posImg.Y / beforeScaleImgHeight * _scaleInternalHeight; Thickness thickness; //缩放时移动 向上/向左 增大/缩小 的值 if (isEnlarge) thickness = new Thickness(ImageMargin.Left - marginX, ImageMargin.Top - marginY, 0, 0); var marginActualX = ImageMargin.Left + marginX; var marginActualY = ImageMargin.Top + marginY; //修正因图片尺寸大于容器尺寸时缩放中心移动而产生的偏移,当某一边缘与容器相接时该边缘不再移动 var right = Abs(beforeScaleImgWidth - ActualWidth + ImageMargin.Left); //靠近左右边缘时 if (Abs(ImageMargin.Left) < baseMarginX || right < baseMarginX) marginActualX = ImageMargin.Left + ImageMargin.Left / (ActualWidth - beforeScaleImgWidth) * _scaleInternalWidth; var bottom = Abs(beforeScaleImgHeight - ActualHeight + ImageMargin.Top); //靠近上下边缘时 if (Abs(ImageMargin.Top) < baseMarginY || bottom < baseMarginY) marginActualY = ImageMargin.Top + ImageMargin.Top / (ActualHeight - beforeScaleImgWidth) * _scaleInternalWidth; //图片尺寸与容器尺寸相近时恢复居中 var subX = ImageWidth - ActualWidth; var subY = ImageHeight - ActualHeight; if (subX < 0.001) marginActualX = (ActualWidth - ImageWidth) / 2; if (subY < 0.001) marginActualY = (ActualHeight - ImageHeight) / 2; thickness = new Thickness(marginActualX, marginActualY, 0, 0); ImageMargin = thickness;

好了,大功告成!

缩放效果如下:

发表回复 取消回复