惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
Vercel News
Vercel News
博客园_首页
Y
Y Combinator Blog
美团技术团队
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
阮一峰的网络日志
阮一峰的网络日志
aimingoo的专栏
aimingoo的专栏
H
Hackread – Cybersecurity News, Data Breaches, AI and More
MyScale Blog
MyScale Blog
GbyAI
GbyAI
人人都是产品经理
人人都是产品经理
T
Tailwind CSS Blog
MongoDB | Blog
MongoDB | Blog
D
DataBreaches.Net
博客园 - Franky
Engineering at Meta
Engineering at Meta
量子位
The GitHub Blog
The GitHub Blog
F
Fortinet All Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
酷 壳 – CoolShell
酷 壳 – CoolShell
N
Netflix TechBlog - Medium

博客园 - Hey,Coder!

微信小程序 web方案实现调音器功能 systemctl slice配置docker最大占用cpu 内存 pipenv环境变量配置 .net8验证码 数据库差异对比工具 docker部署prometheus grafana pgexporter Alertmanager ubuntu24 换源 ssh配置 ubuntu24 Harbor部署 ubuntu24 kvm部署 cockpit web管理界面 .net api代理 Ubuntu 24.04 DMAR IOMMU 报错修复 uniapp iOS App 上架 vs文件软链接 Docker 网络别名 .net8通用中间件处理公共返回值结构 .net环境下OpenTelemetry Body 读取注意事项 jaeger界面显示异常 .net opentelemetry collector jaeger c# opentelemetry 自定义export c# opentelemetry 自定义export c# 生成对象的初始化代码 Elasticsearch ILM 与 Data Stream 概念及实例说明文档 portainer httphelper封装 Ocelot + Consul + SignalR 粘性会话 正交软件架构 docker postgresql17 主从复制 rabbitmq总结与c#示例 docker rabbitmq quartz dashboard
wpf canvas 移动 缩放
Hey,Coder! · 2026-03-13 · via 博客园 - Hey,Coder!

前端

<Canvas x:Name="imageCanvas" 
    Background="Transparent"
    MouseLeftButtonDown="ImageCanvas_MouseLeftButtonDown"
    MouseMove="ImageCanvas_MouseMove"
    MouseLeftButtonUp="ImageCanvas_MouseLeftButtonUp"
    MouseWheel="ImageCanvas_MouseWheel"
    ClipToBounds="True">
    <Image x:Name="imgAnswer" 
           Stretch="Uniform" 
           Source="{Binding Answer.ImgContent}" 
           Visibility="{Binding Answer.ImgContentVisibility}">
        <Image.RenderTransform>
            <MatrixTransform x:Name="imageTransform"/>
        </Image.RenderTransform>
    </Image>
</Canvas>

后台逻辑

    // 状态变量
    private Point _lastMousePosition;
    private bool _isDragging = false;
    private Matrix _matrix = Matrix.Identity;

    // 触摸状态
    private Point? _lastTouchPoint1 = null;
    private Point? _lastTouchPoint2 = null;

    // 缩放限制
    private const double MIN_SCALE = 0.1;
    private const double MAX_SCALE = 10.0;

        #region 鼠标交互

        private void ImageCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (imgAnswer.Visibility != Visibility.Visible || imgAnswer.Source == null)
                return;

            _isDragging = true;
            _lastMousePosition = e.GetPosition(imageCanvas);

            imageCanvas.CaptureMouse();
            imgAnswer.Cursor = Cursors.Hand;
            e.Handled = true;
        }

        private void ImageCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (!_isDragging || e.LeftButton != MouseButtonState.Pressed)
                return;

            if (imgAnswer.Visibility != Visibility.Visible || imgAnswer.Source == null)
                return;

            try
            {
                Point currentPosition = e.GetPosition(imageCanvas);
                double deltaX = currentPosition.X - _lastMousePosition.X;
                double deltaY = currentPosition.Y - _lastMousePosition.Y;

                _matrix.OffsetX += deltaX;
                _matrix.OffsetY += deltaY;
                imageTransform.Matrix = _matrix;

                _lastMousePosition = currentPosition;
                e.Handled = true;
            }
            catch
            {
                // 忽略错误
            }
        }

        private double _currentScale = 1.0; // 当前缩放比例

        private void ImageCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            _isDragging = false;
            imageCanvas.ReleaseMouseCapture();
            imgAnswer.Cursor = Cursors.Arrow;
            e.Handled = true;
        }

        private void ImageCanvas_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            // 计算缩放因子(滚轮向上放大,向下缩小)
            double scaleFactor = e.Delta > 0 ? 1.1 : 0.9;
            _currentScale *= scaleFactor;

            // 获取鼠标当前位置(作为缩放中心)
            Point center = e.GetPosition(imageCanvas);

            // 应用缩放变换到所有笔迹
            ApplyScaleToAllStrokes(scaleFactor, center);

            e.Handled = true;
        }
        // 应用缩放
        private void ApplyScaleToAllStrokes(double scaleFactor, Point center)
        {
            // 创建缩放矩阵(以 center 为中心)
            _matrix.Translate(-center.X, -center.Y);  // 平移到原点
            _matrix.Scale(scaleFactor, scaleFactor);  // 缩放
            _matrix.Translate(center.X, center.Y);   // 平移回原中心

            // 应用变换到笔迹
            imageTransform.Matrix = _matrix;
        }
        #endregion