Яндекс.Метрика

Песочница

Полу-Prezi за 10 минут в .NET и WPF

Речь пойдёт о создании программы для воспроизведения презентации по типу веб-сервиса Prezi, хотя подобную концепцию можно было видеть ранее в PowerPoint 2010 в одном из стандартных темплейтов.

Отличие от обычной презентации в виде слайдов — наличие сплошного фона, как-бы полотна для контента, где перемещение от одной области с содержанием к другой происходит посредством перемещения, приближения и поворота.




Итак, делаем что-то похожее в .NET и WPF. Лично мне нравится VB.NET (наверное из-за отсутствия {...}, которые занимают целую строку в C#).

Необходимо: VisualStudio, Framework 4 и здоровая картинка, например 4800x3800, нарисованная в Фотошопе с каким-то контентом (можно налепить пару слайдов экспортом из PowerPoint):

image

В VisualStudio создаём пустой WPF проект и на «форму» добавляем следующие дела:
фоновую картинку ImageBg с растяжкой под форму, компонент ScrollViewer со скрытыми барами и в него ещё одну картинку Image1 без растяжения.

[Grid Name="Grid1"]
[Image Name="ImageBg" Stretch="Fill"/]
[ScrollViewer HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"]
[Image Name="Image1" Stretch="None"/]
[/ScrollViewer]
[/Grid]

(<> заменено на [])

Задаём переменные:

'базовый путь к файлам
Dim base_dir As String = System.AppDomain.CurrentDomain.BaseDirectory()
'трансформация перемещением
Dim trTranslate As New TranslateTransform
'трансформация поворотом
Dim trRotate As New RotateTransform
'трансформация масштабированием
Dim trScale As New ScaleTransform
'шаг (как-бы слайд)
Dim stp As Integer = 0
'кол-во шагов
Dim stp_count As Integer = 0
'функция нелинейности для анимации перемещения
Dim ease As New PowerEase
'параметры изначального положения (масштаб, угол, координаты)
Dim init_s, init_a As Double
Dim init_x, init_y As Integer
'параметры нового шага (масштаб, угол, координаты и время перемещения)
Dim new_z(99) As Double
Dim new_a(99) As Double
Dim new_x(99) As Double
Dim new_y(99) As Double
Dim new_t(99) As Double


Делаем предзагрузку изображений в Window_Loaded:

ImageBg.Source = New BitmapImage(New Uri(base_dir + "bg.jpg"))
Image1.Source = New BitmapImage(New Uri(base_dir + "content.jpg"))


Файлы bg.jpg с каким-то фоном (например чёрным цветом) и content.jpg нужно положить в папку Debug.
К контенту можно также добавить маску прозрачности .png, но пока это лишнее.

Там же после загрузки изображение прописываем параметры трансформации:

Dim transf_grp As New TransformGroup
transf_grp.Children.Add(trTranslate)
transf_grp.Children.Add(trRotate)
transf_grp.Children.Add(trScale)
Image1.RenderTransform = transf_grp


Т.е. создаётся группа трансформации, в которую входят три отдельных типа трансформации.

Задаём изначальное состояние контента, чтобы большая картинка вписывалась в экран с небольшим зазором (по-человечески это делается свойствами Stretch и Margin, но в данном случае нужно через трансформацию):


init_s = Math.Round(Grid1.ActualWidth / (Image1.Source.Width + 50), 2)
init_a = 0
init_x = 50
init_y = (Grid1.ActualHeight - (Image1.Source.Height) * init_s) * 2
trScale.ScaleX = init_s
trScale.ScaleY = init_s
trRotate.Angle = init_a
trTranslate.X = init_x
trTranslate.Y = init_y


Загрузка списков шагов из файла:

If File.Exists(base_dir + "loc.ini") Then
Dim oRead As System.IO.StreamReader = File.OpenText(base_dir + "loc.ini")
Dim data(99) As String
Dim i As Integer = 0
Do While oRead.Peek >= 0
i += 1
data(i) = oRead.ReadLine()
Dim param() As String = data(i).Split(",")
new_z(i) = CDbl(param(0))
new_a(i) = CDbl(param(1))
new_x(i) = CDbl(param(2))
new_y(i) = CDbl(param(3))
new_t(i) = CDbl(param(4))
If new_z(i) <> 0 Then stp_count += 1
Loop
oRead.Close()
End If


Структура файла: каждая строка — это новый шаг, а в строке через запятую 5 параметров (масштаб, угол, координаты x и y, время перемещения)

Добавляем обработку перемещения между шагами:

Private Sub NavigationStep()
Dim Z_anim As New DoubleAnimation(trScale.ScaleX, new_z(stp), TimeSpan.FromSeconds(new_t(stp)))
Dim A_anim As New DoubleAnimation(trRotate.Angle, new_a(stp), TimeSpan.FromSeconds(new_t(stp) / 2))
Dim X_anim As New DoubleAnimation(trTranslate.X, new_x(stp), TimeSpan.FromSeconds(new_t(stp)))
Dim Y_anim As New DoubleAnimation(trTranslate.Y, new_y(stp), TimeSpan.FromSeconds(new_t(stp)))
ease.EasingMode = EasingMode.EaseInOut
Z_anim.EasingFunction = ease
A_anim.EasingFunction = ease
X_anim.EasingFunction = ease
Y_anim.EasingFunction = ease
trScale.BeginAnimation(ScaleTransform.ScaleXProperty, Z_anim)
trScale.BeginAnimation(ScaleTransform.ScaleYProperty, Z_anim)
trRotate.BeginAnimation(RotateTransform.AngleProperty, A_anim)
trTranslate.BeginAnimation(TranslateTransform.XProperty, X_anim)
trTranslate.BeginAnimation(TranslateTransform.YProperty, Y_anim)
End Sub


Тут просто 4 анимации для масштаба, угла и координат (конечно переменные можно было задать заранее для чистоты кода, но так тоже почти компактно).

Собственно вот и всё по сути.
Осталось записать набор шагов в текстовый файл loc.ini, например:
1.5,0,-70,-40,1.5
2,-100,-1200,-100,2
3,85,110,-640,1
4,35,-735,-1800,0.5

И активировать переключение между шагами в коде:

Private Sub Image1_MouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles Image1.MouseUp
If Not setup_mode And Not sel_mode Then
If e.GetPosition(Grid1).X < Grid1.ActualWidth / 2 Then
stp -= 1
If stp <= 0 Then stp = stp_count
NavigationStep()
End If
If e.GetPosition(Grid1).X >= Grid1.ActualWidth / 2 Then
stp += 1
If stp = stp_count + 1 Then stp = 1
NavigationStep()
End If
End If
End Sub


Если нажали на левую часть изображения контента — на шаг назад, на правую — вперёд.

Для нормальной работы конечно нужен режим настройки с последующим сохранением новых параметров. Но тут его описывать не буду, а рабочую бета-версию можно скачать тут. Для запуска нужен Framework 4.

Также можно совершенствовать программу по многим направлениям — добавить поддержку векторного контента (что более приблизит к Prezi), сделать сохранение проекта, добавить мульти-тач для настройки, быструю навигацию, смену полотна контента и прочее.