В этом разделе мы поговорим о прозрачности. Мы рассмотрим несколько базовых определений и три интересных примеров применения прозрачности. Прозрачность – это степень возможности видеть сквозь материал. Самый легкий способ понять прозрачность это представить себе объект, на который мы смотрим сквозь воду и стекло. Технически, лучи солнца могут проходить сквозь стекло, и таким образом мы можем видеть объекты, находящиеся за ним.
В компьютерной графике мы можем достичь эффекта прозрачности, воспользовавшись альфа-композитингом. Альфа-композитинг это процесс смешивания изображения с задним фоном для создания видимости частичной прозрачности. Процесс композитинга использует альфа канал. Альфа канал это 8-и битный слой в графическом файловом формате, который применяется для выражения светопроницаемости (прозрачности). Дополнительные 8 битов на пиксель служат в качестве маски и представляют собой 256 уровней светопроницаемости. Изображения, используемые в данном учебном материале можно скачать здесь.
Прозрачные прямоугольники
В первом примере мы нарисуем десять прямоугольников с разными уровнями прозрачности.
1 2 3 4 5 6 7 |
def on_draw(self, wid, cr): for i in range(1, 11): cr.set_source_rgba(0, 0, 1, i*0.1) cr.rectangle(50*i, 20, 40, 40) cr.fill() |
Метод set_source_rgba()имеет альфа параметр для обеспечения прозрачности.
1 2 3 4 5 |
for i in range(1, 11): cr.set_source_rgba(0, 0, 1, i*0.1) cr.rectangle(50*i, 20, 40, 40) cr.fill() |
Этот код создает десять прямоугольников с альфа-значениями от 0.1 до 1
Эффект прослойки
В следующем примере мы создадим эффект прослоек. Мы продемонстрируем растущий центрированный текст, который будет постепенно исчезать, начиная с отдельно взятой точки. Это очень простой эффект, который мы зачастую можем наблюдать в flash-анимации.
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
Метод paint_with_alpha() является ключевым в создании данного эффекта
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
#!/usr/bin/python from gi.repository import Gtk, GLib import cairo class cv(object): SPEED = 14 TEXT_SIZE_MAX = 20 ALPHA_DECREASE = 0.01 SIZE_INCREASE = 0.8 class Example(Gtk.Window): def __init__(self): super(Example, self).__init__() self.init_ui() def init_ui(self): self.darea = Gtk.DrawingArea() self.darea.connect("draw", self.on_draw) self.add(self.darea) self.timer = True self.alpha = 1.0 self.size = 1.0 GLib.timeout_add(cv.SPEED, self.on_timer) self.set_title("Puff") self.resize(350, 200) self.set_position(Gtk.WindowPosition.CENTER) self.connect("delete-event", Gtk.main_quit) self.show_all() def on_timer(self): if not self.timer: return False self.darea.queue_draw() return True def on_draw(self, wid, cr): w, h = self.get_size() cr.set_source_rgb(0.5, 0, 0) cr.paint() cr.select_font_face("Courier", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) self.size = self.size + cv.SIZE_INCREASE if self.size > cv.TEXT_SIZE_MAX: self.alpha = self.alpha - cv.ALPHA_DECREASE cr.set_font_size(self.size) cr.set_source_rgb(1, 1, 1) (x, y, width, height, dx, dy) = cr.text_extents("ZetCode") cr.move_to(w/2 - width/2, h/2) cr.text_path("ZetCode") cr.clip() cr.paint_with_alpha(self.alpha) if self.alpha <= 0: self.timer = False def main(): app = Example() Gtk.main() if __name__ == "__main__": main() |
В данном примере мы создадим растущий и исчезающий текст в окне.
1 2 3 4 5 6 |
class cv(object): SPEED = 14 TEXT_SIZE_MAX = 20 ALPHA_DECREASE = 0.01 SIZE_INCREASE = 0.8 |
Здесь мы определим несколько констант, используемых в примере.
1 2 |
self.alpha = 1.0 self.size = 1.0 |
Эти две переменные сохраняют текущие альфа-значение и размер текста.
1 |
GLib.timeout_add(cv.SPEED, self.on_timer) |
Метод on_timer() вызывается каждые 14 миллисекунд.
1 2 3 4 5 6 |
def on_timer(self): if not self.timer: return False self.darea.queue_draw() return True |
В методе on_timer() мы перерисовываем зону виджета при помощи метода queue_draw() .
1 2 3 4 5 6 7 8 9 10 |
def on_draw(self, wid, cr): w, h = self.get_size() cr.set_source_rgb(0.5, 0, 0) cr.paint() cr.select_font_face("Courier", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) ... |
В on_draw() методе мы получаем высоту и ширину области клиента в окне. Эти показатели используются в центре текста. Мы заливаем задний фон окна тёмно-красным цветом. Шрифт текста выбран Courier.
1 |
(x, y, width, height, dx, dy) = cr.text_extents("ZetCode") |
Мы получили текстовые метрики. Мы используем только ширину текста.
1 |
cr.move_to(w/2 - width/2, h/2) |
Мы переходим в область, в которой текст будет центрирован.
1 2 3 |
cr.text_path("ZetCode") cr.clip() cr.paint_with_alpha(self.alpha) |
Ограничим рисование только выбранным путем при помощи метода clip() . Метод paint_with_alpha() закрашивает выбранный исходник в пределах нашей обрезанной области с применением маски альфа-значения.
Отраженное изображение
В данном примере мы продемонстрируем отраженное изображение. Этот эффект создает иллюзию, как если бы наше изображение отражалось в воде или зеркале.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
#!/usr/bin/python from gi.repository import Gtk import cairo import sys class Example(Gtk.Window): def __init__(self): super(Example, self).__init__() self.init_ui() self.load_image() self.init_vars() def init_ui(self): darea = Gtk.DrawingArea() darea.connect("draw", self.on_draw) self.add(darea) self.set_title("Reflection") self.resize(300, 350) self.set_position(Gtk.WindowPosition.CENTER) self.connect("delete-event", Gtk.main_quit) self.show_all() def load_image(self): try: self.s = cairo.ImageSurface.create_from_png("slanec.png") except Exception, e: print e.message sys.exit(1) def init_vars(self): self.imageWidth = self.s.get_width() self.imageHeight = self.s.get_height() self.gap = 40 self.border = 20 def on_draw(self, wid, cr): w, h = self.get_size() lg = cairo.LinearGradient(w/2, 0, w/2, h*3) lg.add_color_stop_rgba(0, 0, 0, 0, 1) lg.add_color_stop_rgba(h, 0.2, 0.2, 0.2, 1) cr.set_source(lg) cr.paint() cr.set_source_surface(self.s, self.border, self.border) cr.paint() alpha = 0.7 step = 1.0 / self.imageHeight cr.translate(0, 2 * self.imageHeight + self.gap) cr.scale(1, -1) i = 0 while(i < self.imageHeight): cr.rectangle(self.border, self.imageHeight-i, self.imageWidth, 1) i = i + 1 cr.save() cr.clip() cr.set_source_surface(self.s, self.border, self.border) alpha = alpha - step cr.paint_with_alpha(alpha) cr.restore() def main(): app = Example() Gtk.main() if __name__ == "__main__": main() |
Отраженное изображение руин замка в окне.
1 2 3 4 5 6 7 |
def load_image(self): try: self.s = cairo.ImageSurface.create_from_png("slanec.png") except Exception, e: print e.message sys.exit(1) |
В методе load_image() изображение является поверхностью, которая была создана из PNG файла.
1 2 3 4 5 6 |
def init_vars(self): self.imageWidth = self.s.get_width() self.imageHeight = self.s.get_height() self.gap = 40 self.border = 20 |
Внутри метода init_vars() мы получаем высоту и ширину изображения. Мы также определяем две переменные.
1 2 3 4 5 6 |
lg = cairo.LinearGradient(w/2, 0, w/2, h*3) lg.add_color_stop_rgba(0, 0, 0, 0, 1) lg.add_color_stop_rgba(h, 0.2, 0.2, 0.2, 1) cr.set_source(lg) cr.paint() |
Задний фон окна заполнен градиентным окрасом. Окрас плавно перетекает из чёрного в темно-серый.
1 2 |
cr.translate(0, 2 * self.imageHeight + self.gap) cr.scale(1, -1) |
Данный код переворачивает изображение и размещает его под исходным изображением. Операция размещения необходима, так как так как операция масштабирования перевернет отражение вверх ногами. Представьте, что вы взяли фотографию, положили её на стол и перевернули.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
i = 0 while(i < self.imageHeight): cr.rectangle(self.border, self.imageHeight-i, self.imageWidth, 1) i = i + 1 cr.save() cr.clip() cr.set_source_surface(self.s, self.border, self.border) alpha = alpha - step cr.paint_with_alpha(alpha) cr.restore() |
Финальная часть раздела, в которой мы сделаем второе изображение прозрачным. Однако прозрачность не является постоянной величиной. Изображение исчезает постепенно. Отраженное изображение прописано линией за линией. Метод clip() задает рисунку прямоугольную форму, с высотой 1. Метод paint_with_alpha() задает прозрачность в расчете во время рисования в текущем обрезке поверхности изображения.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
i = 0 while(i < self.imageHeight): cr.rectangle(self.border, self.imageHeight-i, self.imageWidth, 1) i = i + 1 cr.save() cr.clip() cr.set_source_surface(self.s, self.border, self.border) alpha = alpha - step cr.paint_with_alpha(alpha) cr.restore() |
Демо загрузки
В данном примере мы используем эффект прозрачности для создания демо загрузки. Мы нарисуем восемь линий, которые будут постепенно затухать, создавая эффект движения. Схожие эффекты часто используются для информирования пользователей о загрузке. В качестве примера, приведем потоковое видео в интернете.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
#!/usr/bin/python from gi.repository import Gtk, GLib import cairo import math class cv(object): trs = ( ( 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 ), ( 1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9 ), ( 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8 ), ( 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65 ), ( 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5 ), ( 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3 ), ( 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15 ), ( 0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, ) ) SPEED = 100 CLIMIT = 1000 NLINES = 8 class Example(Gtk.Window): def __init__(self): super(Example, self).__init__() self.init_ui() def init_ui(self): self.darea = Gtk.DrawingArea() self.darea.connect("draw", self.on_draw) self.add(self.darea) self.count = 0 GLib.timeout_add(cv.SPEED, self.on_timer) self.set_title("Waiting") self.resize(250, 150) self.set_position(Gtk.WindowPosition.CENTER) self.connect("delete-event", Gtk.main_quit) self.show_all() def on_timer(self): self.count = self.count + 1 if self.count >= cv.CLIMIT: self.count = 0 self.darea.queue_draw() return True def on_draw(self, wid, cr): cr.set_line_width(3) cr.set_line_cap(cairo.LINE_CAP_ROUND) w, h = self.get_size() cr.translate(w/2, h/2) for i in range(cv.NLINES): cr.set_source_rgba(0, 0, 0, cv.trs[self.count%8][i]) cr.move_to(0.0, -10.0) cr.line_to(0.0, -40.0) cr.rotate(math.pi/4) cr.stroke() def main(): app = Example() Gtk.main() if __name__ == "__main__": main() |
Мы нарисовали восемь линий с восемью разными альфа-параметрами.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class cv(object): trs = ( ( 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 ), ( 1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9 ), ( 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8 ), ( 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65 ), ( 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5 ), ( 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3 ), ( 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15 ), ( 0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, ) ) ... |
Это двухмерный кортеж используемых в демо значений прозрачности. У нас есть восемь строк, каждая из которых будет находиться в своем положении. Все восемь линий будут использовать эти значения беспрерывно.
1 2 3 |
SPEED = 100 CLIMIT = 1000 NLINES = 8 |
Константа SPEED контролирует скорость анимации. Константа CLIMIT является максимальным значением для переменной self.count . По преодолению этого лимита, переменная сбрасывается до показателя 0. Константа NLINES — это количество написанных линий в данном примере.
1 |
GLib.timeout_add(cv.SPEED, self.on_timer) |
Мы воспользуемся функцией таймера для создания анимации. Каждая миллисекунда cv.SPEED указывается в методе on_timer() .
1 2 3 4 5 6 7 8 9 10 |
def on_timer(self): self.count = self.count + 1 if self.count >= cv.CLIMIT: self.count = 0 self.darea.queue_draw() return True |
В методе on_timer() мы увеличиваем переменную self.count . Если переменная достигает показателя константы cv.CLIMIT, то она сбрасывается до 0. Нам не нужно переполнять код и мы не работаем с большими числами.
1 2 3 4 5 |
def on_draw(self, wid, cr): cr.set_line_width(3) cr.set_line_cap(cairo.LINE_CAP_ROUND) ... |
Мы сделаем линии более чуть-более толще, чтобы их было лучше видно и пропишем их с закругленными окончаниями.
1 2 3 |
w, h = self.get_size() cr.translate(w/2, h/2) |
Наш рисунок расположен по центру окна.
1 2 3 4 5 6 7 |
for i in range(cv.NLINES): cr.set_source_rgba(0, 0, 0, cv.trs[self.count%8][i]) cr.move_to(0.0, -10.0) cr.line_to(0.0, -40.0) cr.rotate(math.pi/4) cr.stroke() |
Мы создали восемь линий с разными значениями прозрачности. Линии расположены относительно друг от друга под углом в 45 градусов.
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»