Графический интерфейс пользователя. Анимирование

Электроника

В графических интерфейсах часто используют анимированные объекты, которые меняют свой вид в течение времени. Это позволяет сделать картинку более живой и фокусирует внимание пользователя на важных действиях. Давайте создадим анимированный курсор и добьемся плавности его движения
Глоссарий
Для успешного освоения материала рекомендуем вам изучить следующие понятия:
Курсор
(лат. cursor — бегун; англ. cursor — указатель, стрелка прибора) в интерфейсе пользователя — элемент графического интерфейса, который указывает на объект, с которым будет производиться взаимодействие с помощью клавиатуры, мыши или другого устройства управления. Различают текстовый курсор, обозначающий место ввода с клавиатуры; курсор мыши (или указатель мыши) и других указывающих устройств; курсор меню. Кроме указания на объект курсор может также отображать его состояние, например — невозможность взаимодействия
Графический индикатор
наиболее сложный тип индикаторов, позволяющий передавать как символьную информацию, так и рисунки
Видеолекция
Конспект

Задача
  1. Анимировать курсор, например, добавив вращение
  2. Добиться плавности движения курсора
Требования к виду курсора
  • Две красные линии (l1 и l2), пересекающиеся под углом 90 градусов
  • Курсор вращается по часовой стрелке
  • У каждой линии свой угол относительно горизонтали
  • Для каждой линии важно знать только один конец, а второй можно вычислить по центру курсора
Работа на компьютере
1. Копируем предыдущий проект, переименовываем и открываем

2. Добавляем библиотеку с математическими формулами

3. Добавляем в описание курсора параметр раскадровки
struct Cursor_Data
{
	enum Object id; //идентификатор объекта
	int8_t y;		   	//координаты верхнего левого угла курсора
	int8_t x;			//координаты верхнего левого угла курсора
	int8_t	height;		//высота курсора
	int8_t width;		//ширина курсора
	uint8_t to_redraw;	//флаг "отрисовать"

	//параметры для раскадровки
	uint32_t count_frame; //счетчик кадров
	uint32_t max_count_frame; //максимальное количество кадров - если применимо
	uint32_t period_frame; //"скорость" изменения кадров в периодах тика метода проверки объектов
4. Добавляем константу Pi

5. Убираем лишнюю отрисовку статического курсора, оставляем только вычисление центра

6. Рассчитываем углы и прямоугольник, ограничивающий курсор
// первая линия
	float angle = -(PI/2) + angle_by_frame * cur_struct->count_frame; //угол линии


	float angle_corner = atan(cur_struct->height / cur_struct->width); //тригонометрический угол  для угла прямоугольника курсора

	if(angle >= -angle_corner && angle <= angle_corner)
	{//начало линии на левой границе прямоугольника курсора
		x = cur_struct->x;
		y = y_c - tan(angle) * cur_struct->width/2.0;
	}

	if(angle > angle_corner)
	{//начало линии на верхней границе прямоугольника курсора
		y = cur_struct->y;
		x = x_c - tan(PI/2 - angle) * cur_struct->height/2.0;
	}

	if(angle < -angle_corner)
	{//начало линии на нижней границе прямоугольника курсора
		y = cur_struct->y + cur_struct->height;
		x = x_c - tan(PI/2 + angle) * cur_struct->height/2.0;
	}

	//ветки
	ssd1331_draw_line( x, y ,  x_c + (x_c - x), y_c + (y_c - y) , Color); //первая ветка
7. Аналогично рисуем вторую линию курсора, но угол начинаем от нуля

8. Дополняем значения для курсора. Указываем счетчик кадра с нуля, максимальное количество кадров — 40, период — 5 тиков

9. Организовываем передачу количества тиков, для того чтобы можно было рассчитать раскадровку объекта

10. Для курсора определяем момент времени, когда нужно сменить кадр. Для этого счетчик вызова делим на периодичность смены кадров и вычисляем остаток от деления. Если остаток нулевой, то как раз прошло целое количество тиков для смены кадра

11. Очищаем старое место курсора и запускаем перерисовку
//проверяем наступило ли событие раскадровки объекта
	if(count_check % Cursor_->period_frame ==0)
	{ //наступило время сменить кадр
		Cursor_->Draw_Cursor(Cursor_, 1); //очистка старого места курсора
		check_draw_obj_by_cursor(Object, num_objs, Cursor_); //для объектов, которые перекрывались курсором ставим флаг безусловной перерисовки

		if(Cursor_->count_frame< Cursor_->max_count_frame-1)
			Cursor_->count_frame++; //следующий кадр для отрисовки
		else
			Cursor_->count_frame=0; //следующий кадр для отрисовки

		Cursor_->to_redraw = 1; // нужно потом перерисовать курсор в новом месте
	}
12. В обработке события «раз в 10 мс проверка объектов» формируем счетчик для передачи его в функцию

13. Сохраняем, компилируем
Основные ошибки
1. «Angle_by_frame»
Исправление: в функцию отрисовки добавляем коэффициент, который покажет, на какой угол надо повернуть перекрестье курсора при смене одного кадра

float angle_by_frame = Pi / cur_struct ->max_cout_frame;

2. Типы данных координат
Исправление: исправляем все типы данных на int и добавляем данные для текущих координат x, y
Важно
После прошивки устройства удостоверимся, что курсор меняет скорость движения при пересечении объектов и мигает при вращении
Исправление мигания

1. Рисуем все примитивы в один буфер памяти

2. Перебрасываем графический индикатор одной транзакцией:

  • создаем функцию, которая будет перебрасывать буфер из микроконтроллера в графический индикатор
  • поправляем макрос определения цветов — поменяем местами

3. Модифицируем библиотеку графического индикатора:
  • определяем буфер, куда будут записываться точки примитивов
  • переписываем функцию передачи данных
//мультибайтная передача данных в графический экран
void _sendMultiData(uint8_t* data, uint16_t count)
{
	__SSD1331_DC_SET();
	__SSD1331_CS_CLR();

	HAL_SPI_Transmit(get_hspi1(), data, count, 100);

	__SSD1331_CS_SET();
}
  • пишем переброс буфера в графический индикатор
//копирование данных из буфера в графический индикатор
void SSD1331_UpdateScreen(void)
{


		//set column point
		ssd1331_write_byte(SET_COLUMN_ADDRESS, SSD1331_CMD);
		ssd1331_write_byte(0, SSD1331_CMD);
		ssd1331_write_byte(OLED_WIDTH-1, SSD1331_CMD);

		//set row point
		ssd1331_write_byte(SET_ROW_ADDRESS, SSD1331_CMD);
		ssd1331_write_byte(0, SSD1331_CMD);
		ssd1331_write_byte(OLED_HEIGHT-1, SSD1331_CMD);

		_sendMultiData((uint8_t*)SSD1331_Buffer, OLED_WIDTH*OLED_HEIGHT*2);

 }
  • переписываем функцию, рисующую точку. Будет осуществляться просто запись цвета двух байт в буфер массива, который располагается в микроконтроллере
Тестирование
После запуска видим плавную отрисовку и движение курсора. Отсутствует мигание и неравномерность
Итак, мы добились плавности хода курсора и создали его вращение. Вас ждут задания по теме занятия.
Интерактивное задание
Тест
Для закрепления полученных знаний пройдите тест
Стартуем!
Как для курсора определить момент времени, когда нужно сменить кадр?
Дальше
Проверить
Узнать результат
Как вычислить коэффициент, который покажет на какой угол надо повернуть перекрестье курсора при смене одного кадра?
Дальше
Проверить
Узнать результат
Какие типы данных используются для координат?
Дальше
Проверить
Узнать результат
К сожалению, вы ответили неправильно
Прочитайте лекцию и посмотрите видео еще раз
Пройти еще раз
Неплохо!
Но можно лучше. Прочитайте лекцию и посмотрите видео еще раз
Пройти еще раз
Отлично!
Вы отлично справились. Теперь можете ознакомиться с другими компетенциями
Пройти еще раз