如何在 Blazor 中使用鼠标滚轮放大和缩小图像?

phi*_*lip 4 c# asp.net blazor

我想在 asp.net 上的 blazor 中放大和缩小图像。

当我用于谷歌地图时,我想通过用鼠标滚轮缩放和拖动图像来移动图像位置。(我想使用图像文件,而不是谷歌地图。)

使用示例

有没有办法在 blazor 中放大、缩小和拖动特定图像?

Cob*_*byC 8

笔记:

此组件可让您放大shift同时mouse wheel向上(缩小)或mouse wheel下跌(放大)和按移动图像mouse button 1向下移动时。(它比拖动更平移

限制在Blazor: 在写这篇的时候

  • 目前最大的问题是没有访问鼠标OffsetXOffsetY作为内html元素在这里描述也在这里,所以图像的运动必须只使用CSS来实现。
  • 我用理由移位用于滚动是因为滚动不被堵塞或停用此处描述即使有@onscroll:stopPropagation@onwheel:stopPropagation@onmousewheel:stopPropagation和/或@onscroll:preventDefault@onwheel:preventDefault@onmousewheel:preventDefault父上组mainImageContainer元件。如果内容比可见页面宽,屏幕仍会左右滚动。

解决方案:

缩放部分非常简单,您需要做的就是transform:scale(n);@onmousewheel事件中设置属性。

图像的移动有点复杂,因为没有参考鼠标指针相对于图像或元素边界的位置。(OffsetXOffsetY

我们唯一可以确定的是是否按下了鼠标按钮,然后计算鼠标移动的方向是leftrightupdown

然后的想法是通过将topleftCSS 值设置为百分比来移动元素与图像的位置。

此组件代码:

@using System.Text;

<div id="mainImageContainer" style="display: block;width:@($"{ImageWidthInPx}px");height:@($"{ImageHeightInPx}px");overflow: hidden;">
    <div id="imageMover"
         @onmousewheel="MouseWheelZooming"
         style="@MoveImageStyle">
        <div id="imageContainer"
             @onmousemove="MouseMoving"
             style="@ZoomImageStyle">
            @*this div is used just for moving around when zoomed*@
        </div>
    </div>
</div>
@if (ShowResetButton)
{
    <div style="display:block">
        <button @onclick="ResetImgage">Reset</button>
    </div>
}

@code{

    /// <summary>
    /// The path or url of the image
    /// </summary>
    [Parameter]
    public string ImageUrlPath { get; set; }

    /// <summary>
    /// The width of the image
    /// </summary>
    [Parameter]
    public int ImageWidthInPx { get; set; }

    /// <summary>
    /// The height of the image
    /// </summary>
    [Parameter]
    public int ImageHeightInPx { get; set; }

    /// <summary>
    /// Set to true to show the reset button
    /// </summary>
    [Parameter]
    public bool ShowResetButton { get; set; }

    /// <summary>
    /// Set the amount the image is scaled by, default is 0.1f
    /// </summary>
    [Parameter]
    public double DefaultScaleBy { get; set; } = 0.1f;

    /// <summary>
    /// The Maximum the image can scale to, default = 5f
    /// </summary>
    [Parameter]
    public double ScaleToMaximum { get; set; } = 5f;

    /// <summary>
    /// Set the speed at which the image is moved by, default 2.
    /// 2 or 3 seems to work best.
    /// </summary>
    [Parameter]
    public double DefaultMoveBy { get; set; } = 2;

    //defaults
    double _CurrentScale = 1.0f;
    double _PositionLeft = 0;
    double _PositionTop = 0;
    double _OldClientX = 0;
    double _OldClientY = 0;
    double _DefaultMinPosition = 0;//to the top and left
    double _DefaultMaxPosition = 0;//to the right and down

    //the default settings used to display the image in the child div
    private Dictionary<string, string> _ImageContainerStyles;
    Dictionary<string, string> ImageContainerStyles
    {
        get
        {
            if (_ImageContainerStyles == null)
            {
                _ImageContainerStyles = new Dictionary<string, string>();
                _ImageContainerStyles.Add("width", "100%");
                _ImageContainerStyles.Add("height", "100%");
                _ImageContainerStyles.Add("position", "relative");
                _ImageContainerStyles.Add("background-size", "contain");
                _ImageContainerStyles.Add("background-repeat", "no-repeat");
                _ImageContainerStyles.Add("background-position", "50% 50%");
                _ImageContainerStyles.Add("background-image", $"URL({ImageUrlPath})");
            }
            return _ImageContainerStyles;
        }
    }

    private Dictionary<string, string> _MovingContainerStyles;
    Dictionary<string, string> MovingContainerStyles
    {
        get
        {
            if (_MovingContainerStyles == null)
            {
                InvokeAsync(ResetImgage);
            }
            return _MovingContainerStyles;
        }
    }

    protected async Task ResetImgage()
    {
        _PositionLeft = 0;
        _PositionTop = 0;
        _DefaultMinPosition = 0;
        _DefaultMaxPosition = 0;
        _CurrentScale = 1.0f;

        _MovingContainerStyles = new Dictionary<string, string>();
        _MovingContainerStyles.Add("width", "100%");
        _MovingContainerStyles.Add("height", "100%");
        _MovingContainerStyles.Add("position", "relative");
        _MovingContainerStyles.Add("left", $"{_PositionLeft}%");
        _MovingContainerStyles.TryAdd("top", $"{_PositionTop}%");
    
        await InvokeAsync(StateHasChanged);
    }

    string ZoomImageStyle { get => DictionaryToCss(ImageContainerStyles); }
    string MoveImageStyle { get => DictionaryToCss(MovingContainerStyles); }


    private string DictionaryToCss(Dictionary<string, string> styleDictionary)
    {
        StringBuilder sb = new StringBuilder();
        foreach (var kvp in styleDictionary.AsEnumerable())
        {
            sb.AppendFormat("{0}:{1};", kvp.Key, kvp.Value);
        }
        return sb.ToString();
    }


    protected async void MouseMoving(MouseEventArgs e)
    {
        //if the mouse button 1 is not down exit the function
        if (e.Buttons != 1)
        {
            _OldClientX = e.ClientX;
            _OldClientY = e.ClientY;
            return;
        }

        //get the % of the current scale to move by at least the default move speed plus any scaled changes
        //basically the bigger the image the faster it moves..
        double scaleFrac = (_CurrentScale / ScaleToMaximum);
        double scaleMove = (DefaultMoveBy * (DefaultMoveBy * scaleFrac));

        //moving mouse right
        if (_OldClientX < e.ClientX)
        {
            if ((_PositionLeft - DefaultMoveBy) <= _DefaultMaxPosition)
            {
                _PositionLeft += scaleMove;
            }
        }

        //moving mouse left
        if (_OldClientX > e.ClientX)
        {
            //if (_DefaultMinPosition < (_PositionLeft - DefaultMoveBy))
            if ((_PositionLeft + DefaultMoveBy) >= _DefaultMinPosition)
            {
                _PositionLeft -= scaleMove;
            }
        }

        //moving mouse down
        if (_OldClientY < e.ClientY)
        {
            //if ((_PositionTop + DefaultMoveBy) <= _DefaultMaxPosition)
            if ((_PositionTop - DefaultMoveBy) <= _DefaultMaxPosition)
            {
                _PositionTop += scaleMove;
            }
        }

        //moving mouse up
        if (_OldClientY > e.ClientY)
        {
            //if ((_PositionTop - DefaultMoveBy) > _DefaultMinPosition)
            if ((_PositionTop + DefaultMoveBy) >= _DefaultMinPosition)
            {
                _PositionTop -= scaleMove;
            }
        }

        _OldClientX = e.ClientX;
        _OldClientY = e.ClientY;

        await UpdateScaleAndPosition();
    }

    async Task<double> IncreaseScale()
    {
        return await Task.Run(() =>
        {
            //increase the scale first then calculate the max and min positions
            _CurrentScale += DefaultScaleBy;
            double scaleFrac = (_CurrentScale / ScaleToMaximum);
            double scaleDiff = (DefaultMoveBy + (DefaultMoveBy * scaleFrac));
            double scaleChange = DefaultMoveBy + scaleDiff;
            _DefaultMaxPosition += scaleChange;
            _DefaultMinPosition -= scaleChange;

            return _CurrentScale;
        });
    }

    async Task<double> DecreaseScale()
    {
        return await Task.Run(() =>
        {
            _CurrentScale -= DefaultScaleBy;
           
            double scaleFrac = (_CurrentScale / ScaleToMaximum);
            double scaleDiff = (DefaultMoveBy + (DefaultMoveBy * scaleFrac));
            double scaleChange = DefaultMoveBy + scaleDiff;
            _DefaultMaxPosition -= scaleChange;
            _DefaultMinPosition += scaleChange;//DefaultMoveBy;

            //fix descaling, move the image back into view when descaling (zoomin out)
            if (_CurrentScale <= 1)
            {
                _PositionLeft = 0;
                _PositionTop = 0;
            }
            else
            {
                //left can not be more than max position
                _PositionLeft = (_DefaultMaxPosition < _PositionLeft) ? _DefaultMaxPosition : _PositionLeft;

                //top can not be more than max position
                _PositionTop = (_DefaultMaxPosition < _PositionTop) ? _DefaultMaxPosition : _PositionTop;

                //left can not be less than min position
                _PositionLeft = (_DefaultMinPosition > _PositionLeft) ? _DefaultMinPosition : _PositionLeft;

                //top can not be less than min position
                _PositionTop = (_DefaultMinPosition > _PositionTop) ? _DefaultMinPosition : _PositionTop;
            }
            return _CurrentScale;
        });
    }

    protected async void MouseWheelZooming(WheelEventArgs e)
    {
        //holding shift stops the page from scrolling
        if (e.ShiftKey == true)
        {
            if (e.DeltaY > 0)
            {
                _CurrentScale = ((_CurrentScale + DefaultScaleBy) >= 5) ? _CurrentScale = 5f : await IncreaseScale();
            }
            if (e.DeltaY < 0)
            {
                _CurrentScale = ((_CurrentScale - DefaultScaleBy) <= 0) ? _CurrentScale = DefaultScaleBy : await DecreaseScale();
            }

            await UpdateScaleAndPosition();
        }
    }

    /// <summary>
    /// Refresh the values in the moving style dictionary that is used to position the image.
    /// </summary>    
    async Task UpdateScaleAndPosition()
    {
        await Task.Run(() =>
        {
            if (!MovingContainerStyles.TryAdd("transform", $"scale({_CurrentScale})"))
            {
                MovingContainerStyles["transform"] = $"scale({_CurrentScale})";
            }

            if (!MovingContainerStyles.TryAdd("left", $"{_PositionLeft}%"))
            {
                MovingContainerStyles["left"] = $"{_PositionLeft}%";
            }

            if (!MovingContainerStyles.TryAdd("top", $"{_PositionTop}%"))
            {
                MovingContainerStyles["top"] = $"{_PositionTop}%";
            }
        });
    }

}
Run Code Online (Sandbox Code Playgroud)

这是用法:

@page "/"
@using BlazorWasmApp.Components
Welcome to your new app.

<ZoomableImageComponent ImageUrlPath="images/Capricorn.png"
                        ImageWidthInPx=400
                        ImageHeightInPx=300
                        ShowResetButton=true
                        DefaultScaleBy=0.1f />
Run Code Online (Sandbox Code Playgroud)

这是结果:

在此处输入图片说明

我只在没有触摸输入的台式电脑上用 chrome 测试过这个。