Избегайте перевода при масштабировании полинома и от центра до положения мыши
В моем приложении у меня есть полигон (с 4 точками = прямоугольник) на холсте. Я хочу масштабировать многоугольник с помощью колесика мыши. Для этого я использую этот код:
double scale = 1.0, factor = 1.01, cX, cY;
void polygon_MouseWheel(object sender, MouseWheelEventArgs e)
{
cX = e.GetPosition(polygon).X;
cY = e.GetPosition(polygon).Y;
if (e.Delta > 0) scale *= factor;
else scale /= factor;
polygon.RenderTransform = new ScaleTransform(scale, scale, cX, cY);
}
Это прекрасно работает, когда я помещаю указатель мыши в точку P (скажем, P - точка рядом с верхним левым углом многоугольника) в многоугольнике и прокручиваю вверх или вниз. Таким образом, многоугольник увеличивается или уменьшается в соответствии с этой точкой. Но когда я перемещаю указатель мыши в другую точку Q (возле правого нижнего угла) и продолжаю увеличивать или уменьшать масштаб, многоугольник смещается в направлении верхнего левого угла на величину s. Кажется, что величина s зависит от расстояния между P и Q. Поэтому, если расстояние между P и Q велико, сдвиг велик.
Желаемое поведение в этом случае заключается в том, что многоугольник не смещается, а просто масштабируется до новой точки Q.
У кого-нибудь есть идея, в чем причина сдвига? Я благодарен за любые подсказки.
Пример кода:
<window ...>
<Grid>
<Canvas Name="canvas1" Background="LightBlue">
<Polygon Points="100,100 100,300 300,300 300,100" Name="polygon" Fill="Black" MouseWheel="polygon_MouseWheel"/>
</Canvas>
</Grid>
</Window>
public MainWindow()
{
InitializeComponent();
}
double scale = 1.0, factor = 1.01, cX, cY;
void polygon_MouseWheel(object sender, MouseWheelEventArgs e)
{
cX = e.GetPosition(polygon).X;
cY = e.GetPosition(polygon).Y;
if (e.Delta > 0) scale *= factor;
else scale /= factor;
polygon.RenderTransform = new ScaleTransform(scale, scale, cX, cY);
}
1 ответ
Хорошо, попробовав некоторое время, я выяснил, что вызывает такое поведение. В статье MSDN свойства UIElement.RenderTransform говорится:
Преобразование рендеринга не восстанавливает размер макета или информацию о размере рендеринга. Преобразования рендеринга обычно предназначены для анимации или применения временного эффекта к элементу. Например, элемент может увеличиваться при фокусировке или наведении, или может дрожать при нагрузке, чтобы привлечь внимание к этой части пользовательского интерфейса (UI).
Это означает, что сам многоугольник, соответственно точки многоугольника, не трансформируется. При применении ScaleTransform точки многоугольника не меняются и останутся такими, какими они были установлены в xaml. Тем не менее преобразованный многоугольник будет реагировать на события мыши, но положение мыши отображается в исходных границах.
Пример: Давайте возьмем многоугольник, определенный выше в xaml, и масштабируем его в 1,5 раза. Многоугольник отображается в масштабе, и кажется, что его точки теперь имеют значения (150 150 150 450 450 450 450 150). Но внутренне он все еще имеет старые значения (100 100 100 300 300 300 300 100). Если вы поймете положение указателя мыши в верхнем правом углу, оно будет (300 100) вместо (450 150).
Использование позиции мыши в качестве центра для масштабирования вызывает смещение масштабированного многоугольника.
Решением для этого является масштабирование точек самого многоугольника вместо использования ScaleTransform:
void Scale(double factor, double centerX, double centerY)
{
PointCollection pc = new PointCollection();
foreach (Point p in polygon.Points)
{
Point q = new Point();
q.X = p.X;
q.Y = p.Y;
q.X -= centerX; // translate
q.Y -= centerY;
q.X *= factor; // scale
q.Y *= factor;
q.X += centerX; // translate back
q.Y += centerY;
pc.Add(q);
}
polygon.Points = pc; // polygon is defined in xaml
}
Я не уверен, что это правильный способ сделать это, но он работает для меня.