C# 如何使图像放大&;在Blazor里用鼠标滚轮出去?

C# 如何使图像放大&;在Blazor里用鼠标滚轮出去?,c#,asp.net,blazor,C#,Asp.net,Blazor,我想在asp.net上放大和缩小blazor中的图像 当我使用谷歌地图时,我想通过使用鼠标滚轮缩放和拖动图像来移动图像位置。(我想使用图像文件,而不是谷歌地图。) 有没有办法放大、缩小和拖动blazor中的特定图像?注意: 我只会在上使用它,而不是因为如果网络速度慢,可能会有相当大的延迟 只使用JavaScript和/或可能更容易,但对于这个示例,我决定不使用JS互操作 使用此组件,您可以在按住鼠标滚轮的同时按住shift键进行缩放(缩小),或按住鼠标滚轮的同时按住1键进行移动。(与其说是

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

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

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

注意:

  • 我只会在上使用它,而不是因为如果网络速度慢,可能会有相当大的延迟
  • 只使用JavaScript和/或可能更容易,但对于这个示例,我决定不使用JS互操作
使用此组件,您可以在按住鼠标滚轮的同时按住shift键进行缩放(缩小),或按住鼠标滚轮的同时按住1键进行移动。(与其说是拖动,不如说是平移)

Blazor中的限制:(撰写本文时)

  • 目前最大的问题是无法访问html元素as and中的鼠标
    OffsetX
    OffsetY
    ,因此只能使用CSS来移动图像
  • 我之所以使用Shift进行滚动,是因为即使使用
    @onscroll:stopPropagation
    @onwheel:stopPropagation
    @onmouseheel:stopPropagation
    和/或
    @oncroll:preventDefault
    @onwheel:preventDefault
    ,滚动也不会被阻止或禁用,
    @onmouseheel:preventDefault
    在父
    mainImageContainer
    元素上设置。如果内容比可查看页面宽,屏幕仍将左右滚动
解决方案:

缩放部分非常简单,只需设置
transform:scale(n)@onmouseheel
事件中的code>属性

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

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

然后,通过将
top
left
CSS值设置为百分比,移动元素在图像中的位置

此组件代码:

@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}%";
            }
        });
    }

}
@使用System.Text;
@*此div仅用于缩放时四处移动*@
@如果(显示重置按钮)
{
重置
}
@代码{
/// 
///图像的路径或url
/// 
[参数]
公共字符串ImageUrlPath{get;set;}
/// 
///图像的宽度
/// 
[参数]
公共int ImageWidthInPx{get;set;}
/// 
///图像的高度
/// 
[参数]
public int-ImageHeightInPx{get;set;}
/// 
///设置为true以显示重置按钮
/// 
[参数]
公共bool showretebutton{get;set;}
/// 
///设置图像的缩放量,默认值为0.1f
/// 
[参数]
公共双DefaultScaleBy{get;set;}=0.1f;
/// 
///图像可以缩放到的最大值,默认值为5f
/// 
[参数]
公共双尺度最大值{get;set;}=5f;
/// 
///设置图像移动的速度(默认为2)。
///2或3似乎效果最好。
/// 
[参数]
公共双DefaultMoveBy{get;set;}=2;
//默认值
双电流标度=1.0f;
双位置左=0;
双位置顶部=0;
double _OldClientX=0;
double _OldClientY=0;
双精度_DefaultMinPosition=0;//在顶部和左侧
double _DefaultMaxPosition=0;//向右和向下
//用于在子div中显示图像的默认设置
私人字典(ImageContainerStyles);;
字典图像容器样式
{
得到
{
if(_ImageContainerStyles==null)
{
_ImageContainerStyles=新字典();
_添加(“宽度”,“100%”);
_ImageContainerStyles.添加(“高度”、“100%”);
_添加(“位置”、“相对”);
_添加(“背景大小”、“包含”);
_添加(“背景重复”,“无重复”);
_添加(“背景位置”,“50%50%”);
_Add(“background image”,$“URL({ImageUrlPath})”;
}
返回ImageContainerStyles;
}
}
私人词典(移动集装箱样式);;
字典移动容器样式
{
得到
{
if(_MovingContainerStyles==null)
{
调用同步(重置同步);
}
返回移动集装箱方式;
}
}
受保护的异步任务ResetImgage()
{
_位置左=0;
_PositionTop=0;
_DefaultMinPosition=0;
_DefaultMaxPosition=0;
_电流标度=1.0f;
_MovingContainerStyles=新字典();
_MovingContainerStyles。添加(“宽度”、“100%”);
_MovingContainerStyles.添加(“高度”、“100%”);
_移动集装箱方式。添加(“位置”、“相对”);
_MovingContainerStyles.Add(“left”、$“{u PositionLeft}%”;
_MovingContainerStyles.TryAdd(“top”,“$”{u PositionTop}%”);
等待调用同步(StateHasChanged);
}
字符串ZoomImageStyle{get=>DictionaryToCss(ImageContainerStyles);}
字符串MoveImageStyle{get=>DictionaryToCss(MovingContainerStyles);}
私有字符串字典(字典样式字典)
{
StringBuilder sb=新的StringBuilder();
对于
@page "/"
@using BlazorWasmApp.Components
Welcome to your new app.

<ZoomableImageComponent ImageUrlPath="images/Capricorn.png"
                        ImageWidthInPx=400
                        ImageHeightInPx=300
                        ShowResetButton=true
                        DefaultScaleBy=0.1f />