WPF项目实现Canvas指定对象/点位的平滑缩放动画需求
WPF Canvas 围绕指定对象/点位实现平滑缩放动画
原代码的缩放动画默认以Canvas左上角为中心,原因是ScaleTransform的CenterX和CenterY默认值为0。要实现围绕指定对象或点位的缩放,核心是动态调整缩放中心,并同步修正Canvas的位置偏移,确保目标点在缩放过程中始终保持视觉上的固定位置。
核心思路
- 计算目标点在Canvas坐标系下的绝对位置
- 根据当前缩放比例和目标缩放比例,算出需要调整的偏移量
- 同时对缩放比例和偏移量做动画,实现平滑过渡
场景1:围绕Canvas内的指定UI元素缩放
假设要围绕Canvas中名为targetElement的元素缩放,代码修改如下:
private void btnAnimate_Click(object sender, RoutedEventArgs e) { // 确保Canvas的LayoutTransform是包含平移和缩放的TransformGroup var transformGroup = mCanvas.LayoutTransform as TransformGroup; ScaleTransform scaler; TranslateTransform translator; if (transformGroup == null) { transformGroup = new TransformGroup(); scaler = new ScaleTransform(1.0, 1.0); translator = new TranslateTransform(0, 0); transformGroup.Children.Add(translator); transformGroup.Children.Add(scaler); mCanvas.LayoutTransform = transformGroup; } else { scaler = transformGroup.Children.OfType<ScaleTransform>().First(); translator = transformGroup.Children.OfType<TranslateTransform>().First(); } // 计算目标元素在Canvas中的绝对位置和中心坐标 Point elementTopLeft = targetElement.TransformToAncestor(mCanvas).Transform(new Point(0, 0)); Point targetCenter = new Point( elementTopLeft.X + targetElement.ActualWidth / 2, elementTopLeft.Y + targetElement.ActualHeight / 2 ); // 切换缩放比例(1.0 ↔ 1.5) double targetScale = scaler.ScaleX == 1.0 ? 1.5 : 1.0; // 计算缩放需要的偏移修正量,保证目标中心位置不变 double offsetX = targetCenter.X * (1 - targetScale / scaler.ScaleX); double offsetY = targetCenter.Y * (1 - targetScale / scaler.ScaleY); // 创建带缓动效果的动画,让缩放更自然 var ease = new CubicEase() { EasingMode = EasingMode.EaseInOut }; DoubleAnimation scaleAnim = new DoubleAnimation() { To = targetScale, Duration = TimeSpan.FromMilliseconds(600), EasingFunction = ease }; DoubleAnimation translateXAnim = new DoubleAnimation() { To = translator.X + offsetX, Duration = TimeSpan.FromMilliseconds(600), EasingFunction = ease }; DoubleAnimation translateYAnim = new DoubleAnimation() { To = translator.Y + offsetY, Duration = TimeSpan.FromMilliseconds(600), EasingFunction = ease }; // 启动所有动画 scaler.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAnim); scaler.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAnim); translator.BeginAnimation(TranslateTransform.XProperty, translateXAnim); translator.BeginAnimation(TranslateTransform.YProperty, translateYAnim); }
场景2:围绕鼠标点击的点位缩放
如果要围绕鼠标点击位置缩放,先给Canvas绑定MouseDown事件,再实现缩放逻辑:
// Canvas的MouseDown事件处理 private void mCanvas_MouseDown(object sender, MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Left) { ZoomToPoint(e.GetPosition(mCanvas)); } } // 围绕指定点位缩放的核心方法 private void ZoomToPoint(Point targetPoint) { var transformGroup = mCanvas.LayoutTransform as TransformGroup; ScaleTransform scaler; TranslateTransform translator; if (transformGroup == null) { transformGroup = new TransformGroup(); scaler = new ScaleTransform(1.0, 1.0); translator = new TranslateTransform(0, 0); transformGroup.Children.Add(translator); transformGroup.Children.Add(scaler); mCanvas.LayoutTransform = transformGroup; } else { scaler = transformGroup.Children.OfType<ScaleTransform>().First(); translator = transformGroup.Children.OfType<TranslateTransform>().First(); } double targetScale = scaler.ScaleX == 1.0 ? 1.5 : 1.0; double offsetX = targetPoint.X * (1 - targetScale / scaler.ScaleX); double offsetY = targetPoint.Y * (1 - targetScale / scaler.ScaleY); var ease = new CubicEase() { EasingMode = EasingMode.EaseInOut }; DoubleAnimation scaleAnim = new DoubleAnimation() { To = targetScale, Duration = TimeSpan.FromMilliseconds(600), EasingFunction = ease }; DoubleAnimation translateXAnim = new DoubleAnimation() { To = translator.X + offsetX, Duration = TimeSpan.FromMilliseconds(600), EasingFunction = ease }; DoubleAnimation translateYAnim = new DoubleAnimation() { To = translator.Y + offsetY, Duration = TimeSpan.FromMilliseconds(600), EasingFunction = ease }; scaler.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAnim); scaler.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAnim); translator.BeginAnimation(TranslateTransform.XProperty, translateXAnim); translator.BeginAnimation(TranslateTransform.YProperty, translateYAnim); }
关键说明
- 使用
TransformGroup组合TranslateTransform和ScaleTransform,保证先平移再缩放的顺序,偏移修正才会正确 - 偏移量计算公式
目标点坐标 × (1 - 目标缩放比例 / 当前缩放比例),能确保目标点在缩放前后视觉位置不变 - 添加
EasingFunction可以避免生硬的线性动画,让缩放过程更流畅自然
内容的提问来源于stack exchange,提问作者Gokhan
相关产品推荐
相关产品推荐

