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

推荐订阅源

Jina AI
Jina AI
V
Vulnerabilities – Threatpost
Security Latest
Security Latest
AI
AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
量子位
H
Help Net Security
Attack and Defense Labs
Attack and Defense Labs
The GitHub Blog
The GitHub Blog
L
LINUX DO - 最新话题
A
Arctic Wolf
博客园_首页
S
Securelist
S
Secure Thoughts
Google DeepMind News
Google DeepMind News
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Tailwind CSS Blog
Apple Machine Learning Research
Apple Machine Learning Research
酷 壳 – CoolShell
酷 壳 – CoolShell
Stack Overflow Blog
Stack Overflow Blog
N
Netflix TechBlog - Medium
Cyberwarzone
Cyberwarzone
小众软件
小众软件
T
Threatpost
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Blog — PlanetScale
Blog — PlanetScale
N
News and Events Feed by Topic
NISL@THU
NISL@THU
Forbes - Security
Forbes - Security
博客园 - 聂微东
F
Fortinet All Blogs
Simon Willison's Weblog
Simon Willison's Weblog
H
Heimdal Security Blog
罗磊的独立博客
S
Security @ Cisco Blogs
B
Blog
T
Troy Hunt's Blog
Engineering at Meta
Engineering at Meta
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
The Hacker News
The Hacker News
The Last Watchdog
The Last Watchdog
Hacker News - Newest:
Hacker News - Newest: "LLM"
I
Intezer
T
Threat Research - Cisco Blogs
C
Cybersecurity and Infrastructure Security Agency CISA
The Cloudflare Blog
S
Schneier on Security
月光博客
月光博客
L
LINUX DO - 热门话题
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org

博客园 - Hey,Coder!

Elasticsearch ILM 与 Data Stream 概念及实例说明文档 portainer httphelper封装 Ocelot + Consul + SignalR 粘性会话 正交软件架构 docker postgresql17 主从复制 rabbitmq总结与c#示例 docker rabbitmq quartz dashboard docker consul registrator 服务自动注册consul 微服务动态扩容 ubuntu24.04 安装docker RAG 检索增强生成 软考 - 架构设计师 知识点总结 c# MailKit3.4.3 发送邮件、附件 nginx 反向代理postgresql c# 信号量 elsa 3.5 中间件记录最后执行的节点数据 xp密钥 c# System.Text.Json 反序列化Dictionary<string,object>时未转换基础类型的处理方法 docker配置代理 openclaw 接入 LMStudio的模型服务 windows平台openclaw搭建 wpf scrollerview触摸滚动 c# 动态切换sqlsugar连接字符串 c# Elastic.Clients.Elasticsearch 动态查询 c# es 封装Elastic.Clients.Elasticsearch c# scrollerview滚动到指定元素位置 WPF 重写Expander 基于elsa工作流封装一套变量、组件的体系 常用活动重写 DynamicExpresso 轻量级动态表达式库 c# elsa 3.5.2 程序化工作流常用功能及自定义中间件 leaflet docker 连接老版本的sqlserver ssl错误 c# quartz 动态创建任务 控制任务运行和停止 依赖注入 cefsharp 模拟点击 批量重置数据库的数据 docker 复制远程镜像本地并创建容器 c# newtonsoft dynamic
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