Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11468 глав6256 карточек справочника
Дерево руководства

Глава 427 · После первых изображений Gmagick, объектов, массивов и циклов

Gmagick: время кадров, повторы и удаление при анимации

Управлять временем показа, повтором GIF и состоянием холста после каждого кадра.

Сравнить спокойный и быстрый показ

Одинаковые картинки могут восприниматься как торопливое мигание или спокойная демонстрация. Сравним короткую и длинную паузу каждого ракурса. Задержка30 означает30/100 секунды, а не30 секунд и не30 миллисекунд.

Первый исходный ракурс: коробка слева
Первый исходный ракурс: коробка слева — 320 × 200 px.
Сравнить спокойный и быстрый показ — анимированный GIF: 200ms, 200ms, 200ms; dispose 1,1,1; поле loop=0
Сравнить спокойный и быстрый показ — анимированный GIF: 200ms, 200ms, 200ms; dispose 1,1,1; поле loop=0 — 320 × 200 px.
Сравнить спокойный и быстрый показ — анимированный GIF: 800ms, 800ms, 800ms; dispose 1,1,1; поле loop=0
Сравнить спокойный и быстрый показ — анимированный GIF: 800ms, 800ms, 800ms; dispose 1,1,1; поле loop=0 — 320 × 200 px.
Один и тот же первый кадр для двух скоростей
Один и тот же первый кадр для двух скоростей — 320 × 200 px.
<?php
$sequence = new Gmagick(__DIR__ . '/views.gif');
$sequence->setImageIndex(0);
do {
    $sequence->setImageDelay(80);
} while ($sequence->nextImage());
$sequence->setImageIndex(0);
$sequence->writeImage(__DIR__ . '/slow.gif', true);

setImageDelay изменяет активный кадр. Цикл повторяет настройку для всех изображений.80 сотых —0.8 секунды. Перед экспортом указатель снова ставится в начало. Полная программа читает getter для каждого кадра и проверяет записанный GIF, поэтому речь идёт о времени в файле, а не только о присвоении переменной.

Сравнить спокойный и быстрый показСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$sequence=new Gmagick();
$sequence->readImageBlob(file_get_contents(__DIR__.'/views.ppm'));
$sequence->setImageIndex(0);
do{$sequence->setImageDelay(30);$sequence->setImageDispose(Gmagick::DISPOSE_NONE);$sequence->setImageIterations(0);}while($sequence->nextImage());
$sequence->setImageIndex(0);
$sequence->setImageIndex(0);
$sequence->setImageDepth(8);
$sequence->setImageFormat('PNG');
$sequence->writeImage(__DIR__.'/gm100-speed-frame.png',false);
$delays=[];
foreach([20,80] as $delay){
    $sequence->setImageIndex(0);$seen=[];
    do{$sequence->setImageDelay($delay);$seen[]=$sequence->getImageDelay();$sequence->setImageFormat('GIF');}while($sequence->nextImage());
    $delays[$delay]=$seen;$sequence->setImageIndex(0);
    $sequence->writeImage(__DIR__.'/gm100-speed'.$delay.'.gif',true);
}

gmReport('gm100-time-speed',["gmagick.construct", "gmagick.readimageblob", "gmagick.setimageindex", "gmagick.setimagedelay", "gmagick.setimagedispose", "gmagick.setimageiterations", "gmagick.nextimage", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage", "gmagick.getimagedelay"],["gm100-speed-frame.png"],
    ['fast_delays'=>$delays[20]===[20,20,20],'slow_delays'=>$delays[80]===[80,80,80],'size_gm100_speed_frame'=>gmInspect('gm100-speed-frame.png')['width']===320&&gmInspect('gm100-speed-frame.png')['height']===200],
    $errors ?? [], ['delays_centiseconds'=>$delays]);
echo "gm100-speed-frame.png 320x200\n";
Ожидаемый результат
gm100-speed-frame.png 320x200

Самостоятельный ZIP: php example.php с Gmagick. Окружение: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные PPM и готовый views.gif восстанавливаются из .b64. gm-fixture.php проверяет файлы и записывает metrics.json; изучаемые операции видны в самой программе. PNG и GIF сохраняются рядом.

Вывод успешного запуска
gm100-speed-frame.png 320x200

Браузер планирует показ кадров самостоятельно, особенно для очень малых задержек и фоновых вкладок. В примерах выбраны заметные положительные интервалы; нулевая задержка не используется как обещание невидимого промежуточного кадра.

Задержать последний ракурс

Последний ракурс должен задержаться, чтобы покупатель успел рассмотреть товар. Назначим отдельное время каждому кадру: два коротких перехода и длинная финальная пауза. Никаких копий последнего кадра ради паузы не требуется.

Первый исходный ракурс: коробка слева
Первый исходный ракурс: коробка слева — 320 × 200 px.
Задержать последний ракурс — анимированный GIF: 200ms, 200ms, 1200ms; dispose 1,1,1; поле loop=0
Задержать последний ракурс — анимированный GIF: 200ms, 200ms, 1200ms; dispose 1,1,1; поле loop=0 — 320 × 200 px.
Последний ракурс остаётся на экране1.2s
Последний ракурс остаётся на экране1.2s — 320 × 200 px.
<?php
$sequence = new Gmagick(__DIR__ . '/views.gif');
foreach ([20, 20, 120] as $index => $delay) {
    $sequence->setImageIndex($index);
    $sequence->setImageDelay($delay);
}
$sequence->setImageIndex(0);
$sequence->writeImage(__DIR__ . '/pause.gif', true);

Массив содержит три задержки. foreach выдаёт индекс и значение; стрелка => внутри foreach связывает ключ с переменной, это другая запись, чем объектная ->.120 сотых —1.2 секунды. Полная программа читает getImageDelay обратно и показывает последний кадр отдельным PNG.

Задержать последний ракурсСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$sequence=new Gmagick();
$sequence->readImageBlob(file_get_contents(__DIR__.'/views.ppm'));
$sequence->setImageIndex(0);
do{$sequence->setImageDelay(30);$sequence->setImageDispose(Gmagick::DISPOSE_NONE);$sequence->setImageIterations(0);}while($sequence->nextImage());
$sequence->setImageIndex(0);
$seen=[];
foreach([20,20,120] as $i=>$delay){$sequence->setImageIndex($i);$sequence->setImageDelay($delay);$seen[]=$sequence->getImageDelay();}
$sequence->setImageIndex(2);
$sequence->setImageDepth(8);
$sequence->setImageFormat('PNG');
$sequence->writeImage(__DIR__.'/gm100-pause-last.png',false);
$sequence->setImageIndex(0);
do{$sequence->setImageFormat('GIF');}while($sequence->nextImage());
$sequence->setImageIndex(0);$sequence->writeImage(__DIR__.'/gm100-pause.gif',true);

gmReport('gm100-time-pauses',["gmagick.construct", "gmagick.readimageblob", "gmagick.setimageindex", "gmagick.setimagedelay", "gmagick.setimagedispose", "gmagick.setimageiterations", "gmagick.nextimage", "gmagick.getimagedelay", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage"],["gm100-pause-last.png"],
    ['delays'=>$seen===[20,20,120],'size_gm100_pause_last'=>gmInspect('gm100-pause-last.png')['width']===320&&gmInspect('gm100-pause-last.png')['height']===200],
    $errors ?? [], ['delays_centiseconds'=>$seen,'cycle_duration_seconds'=>1.6]);
echo "gm100-pause-last.png 320x200\n";
Ожидаемый результат
gm100-pause-last.png 320x200

Самостоятельный ZIP: php example.php с Gmagick. Окружение: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные PPM и готовый views.gif восстанавливаются из .b64. gm-fixture.php проверяет файлы и записывает metrics.json; изучаемые операции видны в самой программе. PNG и GIF сохраняются рядом.

Вывод успешного запуска
gm100-pause-last.png 320x200

Длина массива должна совпадать с числом кадров. Если последовательность была сокращена removeImage, старый набор задержек надо пересчитать: индекс теперь относится к новому списку.

Сравнить непрерывный цикл и конечный повтор

Для короткого объявления достаточно конечного числа повторов, для демонстрации ракурсов нужен непрерывный цикл. Сравним две настройки iterations на одинаковых изображениях. Время кадра оставим одинаковым, чтобы не спутать скорость с количеством проходов.

Первый исходный ракурс: коробка слева
Первый исходный ракурс: коробка слева — 320 × 200 px.
Сравнить непрерывный цикл и конечный повтор — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0
Сравнить непрерывный цикл и конечный повтор — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0 — 320 × 200 px.
Сравнить непрерывный цикл и конечный повтор — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=2
Сравнить непрерывный цикл и конечный повтор — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=2 — 320 × 200 px.
Статический последний кадр конечной анимации
Статический последний кадр конечной анимации — 320 × 200 px.
<?php
$sequence = new Gmagick(__DIR__ . '/views.gif');
$sequence->setImageIndex(0);
$sequence->setImageIterations(2);
$iterations = $sequence->getImageIterations();
$sequence->setImageFormat('GIF');
$sequence->writeImage(__DIR__ . '/limited.gif', true);

Здесь значение2 относится к циклу, а не к сотым секунды. Настройка задаётся на первом кадре перед экспортом. Для бесконечного варианта используется0. Полная программа отдельно фиксирует getter Gmagick и фактически записанное поле повтора GIF; эти числа не следует автоматически считать одинаково определёнными во всех инструментах.

Сравнить непрерывный цикл и конечный повторСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$sequence=new Gmagick();
$sequence->readImageBlob(file_get_contents(__DIR__.'/views.ppm'));
$sequence->setImageIndex(0);
do{$sequence->setImageDelay(30);$sequence->setImageDispose(Gmagick::DISPOSE_NONE);$sequence->setImageIterations(0);}while($sequence->nextImage());
$sequence->setImageIndex(0);
$iterations=[];
foreach([0,2] as $loops){
    $sequence->setImageIndex(0);$sequence->setImageIterations($loops);$iterations[]=$sequence->getImageIterations();
    do{$sequence->setImageFormat('GIF');}while($sequence->nextImage());$sequence->setImageIndex(0);
    $sequence->writeImage(__DIR__.'/gm100-loops'.$loops.'.gif',true);
}
$sequence->setImageIndex(2);
$sequence->setImageDepth(8);
$sequence->setImageFormat('PNG');
$sequence->writeImage(__DIR__.'/gm100-loops-last.png',false);
$read=new Gmagick(__DIR__.'/gm100-loops2.gif');$read->setImageIndex(0);$readIterations=$read->getImageIterations();

gmReport('gm100-time-loops',["gmagick.construct", "gmagick.readimageblob", "gmagick.setimageindex", "gmagick.setimagedelay", "gmagick.setimagedispose", "gmagick.setimageiterations", "gmagick.nextimage", "gmagick.getimageiterations", "gmagick.setimageformat", "gmagick.writeimage", "gmagick.setimagedepth"],["gm100-loops-last.png"],
    ['iterations'=>$iterations===[0,2],'readback_first'=>$readIterations===2,'size_gm100_loops_last'=>gmInspect('gm100-loops-last.png')['width']===320&&gmInspect('gm100-loops-last.png')['height']===200],
    $errors ?? [], ['configured_iterations'=>$iterations,'readback_first_iterations'=>$readIterations,'raw_netscape_loop'=>2]);
echo "gm100-loops-last.png 320x200\n";
Ожидаемый результат
gm100-loops-last.png 320x200

Самостоятельный ZIP: php example.php с Gmagick. Окружение: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные PPM и готовый views.gif восстанавливаются из .b64. gm-fixture.php проверяет файлы и записывает metrics.json; изучаемые операции видны в самой программе. PNG и GIF сохраняются рядом.

Вывод успешного запуска
gm100-loops-last.png 320x200

Конечная анимация со временем останавливается, поэтому рядом оставлен статический итоговый кадр. Для повторного просмотра можно открыть сам GIF заново. Текст PHP.net о задержке в параметре iterations ошибочен; единицы времени принадлежат setImageDelay.

В сохранённом примере iterations2 прочиталось Gmagick как2, а расширение NETSCAPE GIF содержит поле loop2. Это поле счётчика повторов, и его нельзя без уточнения трактовать как ровно два полных показа во всех просмотрщиках. Значение0 означает бесконечный цикл. Для iterations1 исследованный кодер вообще не записал расширение повтора. Последующие кадры при чтении GIF имеют собственное значение iterations1; параметр цикла контролируем по первому кадру.

Сравнить удаление на полных кадрах

После показа кадра просмотрщик должен решить, что оставить на холсте. Сначала возьмём полные непрозрачные изображения320×200: следующий кадр закрывает весь предыдущий, поэтому разные способы удаления могут дать одинаковую видимую последовательность.

Первый исходный ракурс: коробка слева
Первый исходный ракурс: коробка слева — 320 × 200 px.
Сравнить удаление на полных кадрах — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0
Сравнить удаление на полных кадрах — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0 — 320 × 200 px.
Сравнить удаление на полных кадрах — анимированный GIF: 300ms, 300ms, 300ms; dispose 2,2,2; поле loop=0
Сравнить удаление на полных кадрах — анимированный GIF: 300ms, 300ms, 300ms; dispose 2,2,2; поле loop=0 — 320 × 200 px.
Полный последний кадр закрывает весь предыдущий холст
Полный последний кадр закрывает весь предыдущий холст — 320 × 200 px.
<?php
$sequence = new Gmagick(__DIR__ . '/views.gif');
$sequence->setImageIndex(0);
do {
    $sequence->setImageDispose(Gmagick::DISPOSE_BACKGROUND);
} while ($sequence->nextImage());
$sequence->setImageIndex(0);
$sequence->writeImage(__DIR__ . '/background.gif', true);

DISPOSE_BACKGROUND — именованная константа со значением2. Она просит восстановить фон занятой кадром области после его показа. DISPOSE_NONE со значением1 оставляет изображение на месте. Getter getImageDispose читает настройку текущего кадра; список кадров от этой настройки не сокращается.

Сравнить удаление на полных кадрахСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$sequence=new Gmagick();
$sequence->readImageBlob(file_get_contents(__DIR__.'/views.ppm'));
$sequence->setImageIndex(0);
do{$sequence->setImageDelay(30);$sequence->setImageDispose(Gmagick::DISPOSE_NONE);$sequence->setImageIterations(0);}while($sequence->nextImage());
$sequence->setImageIndex(0);
$settings=[];
foreach([Gmagick::DISPOSE_NONE,Gmagick::DISPOSE_BACKGROUND] as $dispose){
    $sequence->setImageIndex(0);$seen=[];
    do{$sequence->setImageDispose($dispose);$seen[]=$sequence->getImageDispose();$sequence->setImageFormat('GIF');}while($sequence->nextImage());
    $settings[$dispose]=$seen;$sequence->setImageIndex(0);
    $sequence->writeImage(__DIR__.'/gm100-full-dispose'.$dispose.'.gif',true);
}
$sequence->setImageIndex(2);
$sequence->setImageDepth(8);
$sequence->setImageFormat('PNG');
$sequence->writeImage(__DIR__.'/gm100-full-last.png',false);

gmReport('gm100-time-full-dispose',["gmagick.construct", "gmagick.readimageblob", "gmagick.setimageindex", "gmagick.setimagedelay", "gmagick.setimagedispose", "gmagick.setimageiterations", "gmagick.nextimage", "gmagick.getimagedispose", "gmagick.setimageformat", "gmagick.writeimage", "gmagick.setimagedepth"],["gm100-full-last.png"],
    ['none_codes'=>$settings[1]===[1,1,1],'background_codes'=>$settings[2]===[2,2,2],'size_gm100_full_last'=>gmInspect('gm100-full-last.png')['width']===320&&gmInspect('gm100-full-last.png')['height']===200],
    $errors ?? [], ['disposals'=>$settings]);
echo "gm100-full-last.png 320x200\n";
Ожидаемый результат
gm100-full-last.png 320x200

Самостоятельный ZIP: php example.php с Gmagick. Окружение: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные PPM и готовый views.gif восстанавливаются из .b64. gm-fixture.php проверяет файлы и записывает metrics.json; изучаемые операции видны в самой программе. PNG и GIF сохраняются рядом.

Вывод успешного запуска
gm100-full-last.png 320x200

Одинаковая картинка в этом сравнении не означает, что dispose не работает. Полноразмерные непрозрачные кадры скрывают разницу. Следующий пример использует частичные области, где предыдущее состояние холста становится существенным.

Увидеть влияние удаления на частичные кадры

У компактной анимации последующие кадры могут содержать только изменившуюся область. Для такого файла уже важно, что произошло с остальной частью холста после предыдущего кадра. Сравним оставление, фон и восстановление прежнего содержимого.

Первый исходный ракурс: коробка слева
Первый исходный ракурс: коробка слева — 320 × 200 px.
Увидеть влияние удаления на частичные кадры — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0
Увидеть влияние удаления на частичные кадры — анимированный GIF: 300ms, 300ms, 300ms; dispose 1,1,1; поле loop=0 — 320 × 200 px.
Увидеть влияние удаления на частичные кадры — анимированный GIF: 300ms, 300ms, 300ms; dispose 2,2,2; поле loop=0
Увидеть влияние удаления на частичные кадры — анимированный GIF: 300ms, 300ms, 300ms; dispose 2,2,2; поле loop=0 — 320 × 200 px.
Увидеть влияние удаления на частичные кадры — анимированный GIF: 300ms, 300ms, 300ms; dispose 3,3,3; поле loop=0
Увидеть влияние удаления на частичные кадры — анимированный GIF: 300ms, 300ms, 300ms; dispose 3,3,3; поле loop=0 — 320 × 200 px.
NONE: сохранён фон, на финальном кадре одна коробка справа
NONE: сохранён фон, на финальном кадре одна коробка справа — 320 × 200 px.
BACKGROUND: в финальном кадре пропала неподвижная полка
BACKGROUND: в финальном кадре пропала неподвижная полка — 320 × 200 px.
PREVIOUS: в финальном кадре осталась старая коробка слева
PREVIOUS: в финальном кадре осталась старая коробка слева — 320 × 200 px.
<?php
$sequence = new Gmagick(__DIR__ . '/views.gif');
$patches = $sequence->deconstructImages();
$patches->setImageIndex(0);
do {
    $patches->setImageDispose(Gmagick::DISPOSE_NONE);
} while ($patches->nextImage());
$patches->setImageIndex(0); $patches->writeImage(__DIR__ . '/patches.gif', true);

deconstructImages возвращает новый список областей отличий. Каждая область имеет размеры и положение на общем холсте. DISPOSE_PREVIOUS со значением3 возвращает содержимое области, бывшее до её показа. Полная программа сохраняет анимации и отдельные собранные кадры, чтобы различие было видно без угадывания момента.

Увидеть влияние удаления на частичные кадрыСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$sequence=new Gmagick();
$sequence->readImageBlob(file_get_contents(__DIR__.'/views.ppm'));
$sequence->setImageIndex(0);
do{$sequence->setImageDelay(30);$sequence->setImageDispose(Gmagick::DISPOSE_NONE);$sequence->setImageIterations(0);}while($sequence->nextImage());
$sequence->setImageIndex(0);
$settings=[];
foreach([Gmagick::DISPOSE_NONE,Gmagick::DISPOSE_BACKGROUND,Gmagick::DISPOSE_PREVIOUS] as $dispose){
    $patches=$sequence->deconstructImages();$patches->setImageIndex(0);$seen=[];
    do{$patches->setImageDispose($dispose);$seen[]=$patches->getImageDispose();$patches->setImageFormat('GIF');}while($patches->nextImage());
    $settings[$dispose]=$seen;$patches->setImageIndex(0);$name='gm100-patch-dispose'.$dispose;
    $patches->writeImage(__DIR__.'/'.$name.'.gif',true);
    $read=new Gmagick(__DIR__.'/'.$name.'.gif');$display=$read->coalesceImages();$display->setImageIndex(2);
    $display->setImageDepth(8);$display->setImageFormat('PNG');$display->writeImage(__DIR__.'/'.$name.'.png',false);
}

gmReport('gm100-time-patch-dispose',["gmagick.construct", "gmagick.readimageblob", "gmagick.setimageindex", "gmagick.setimagedelay", "gmagick.setimagedispose", "gmagick.setimageiterations", "gmagick.nextimage", "gmagick.deconstructimages", "gmagick.getimagedispose", "gmagick.setimageformat", "gmagick.writeimage", "gmagick.coalesceimages", "gmagick.setimagedepth"],["gm100-patch-dispose1.png", "gm100-patch-dispose2.png", "gm100-patch-dispose3.png"],
    ['none_codes'=>$settings[1]===[1,1,1],'background_codes'=>$settings[2]===[2,2,2],'previous_codes'=>$settings[3]===[3,3,3],'size_gm100_patch_dispose1'=>gmInspect('gm100-patch-dispose1.png')['width']===320&&gmInspect('gm100-patch-dispose1.png')['height']===200,'size_gm100_patch_dispose2'=>gmInspect('gm100-patch-dispose2.png')['width']===320&&gmInspect('gm100-patch-dispose2.png')['height']===200,'size_gm100_patch_dispose3'=>gmInspect('gm100-patch-dispose3.png')['width']===320&&gmInspect('gm100-patch-dispose3.png')['height']===200],
    $errors ?? [], ['disposals'=>$settings,'pngs'=>'native coalesce of the saved GIF, final frame']);
echo "gm100-patch-dispose1.png 320x200 gm100-patch-dispose2.png 320x200 gm100-patch-dispose3.png 320x200\n";
Ожидаемый результат
gm100-patch-dispose1.png 320x200 gm100-patch-dispose2.png 320x200 gm100-patch-dispose3.png 320x200

Самостоятельный ZIP: php example.php с Gmagick. Окружение: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные PPM и готовый views.gif восстанавливаются из .b64. gm-fixture.php проверяет файлы и записывает metrics.json; изучаемые операции видны в самой программе. PNG и GIF сохраняются рядом.

Вывод успешного запуска
gm100-patch-dispose1.png 320x200 gm100-patch-dispose2.png 320x200 gm100-patch-dispose3.png 320x200

Нельзя считать обрезанный фрагмент самостоятельным полноразмерным кадром. Нельзя также произвольно менять dispose после сокращения областей и обещать прежнюю анимацию: геометрия, предыдущий холст и удаление образуют единый порядок действий.

Здесь отдельные PNG получены самой библиотекой: coalesceImages собирает отображаемые полные кадры из записанного GIF с учётом областей и dispose. Для NONE полка остаётся; для BACKGROUND она пропадает; для PREVIOUS слева сохраняется старая коробка. Это намеренно разные результаты одного набора фрагментов. GIF89a также определяет0 как неуказанный способ, а4…7 резервирует. Формулировка «удаление кадра» здесь относится к холсту после показа; removeImage удаляет элемент списка и решает другую задачу.

Настроить показ функцией каталога

Объединим настройку демонстрации в функцию: кадры, массив задержек, число повторов и способ удаления. Функция принимает проверяемые значения и оставляет вызывающему коду явный файл результата. На странице виден готовый цикл с длинной финальной паузой.

Первый исходный ракурс: коробка слева
Первый исходный ракурс: коробка слева — 320 × 200 px.
Настроить показ функцией каталога — анимированный GIF: 300ms, 300ms, 1200ms; dispose 1,1,1; поле loop=0
Настроить показ функцией каталога — анимированный GIF: 300ms, 300ms, 1200ms; dispose 1,1,1; поле loop=0 — 320 × 200 px.
Функция задаёт финальную паузу и сохраняет полку
Функция задаёт финальную паузу и сохраняет полку — 320 × 200 px.
<?php
$sequence = new Gmagick(__DIR__ . '/views.gif');
$sequence->setImageIndex(2);
$sequence->setImageDelay(120);
$sequence->setImageDispose(Gmagick::DISPOSE_NONE);
$sequence->setImageIndex(0);
$sequence->setImageIterations(0);
$sequence->writeImage(__DIR__ . '/catalog.gif', true);

Малый фрагмент показывает сочетание трёх настроек. Полная функция проверяет число задержек, положительное время и поддерживаемый нашей задачей dispose. Это ограничения функции каталога, не объявление всех возможностей библиотеки. Возвраты setters — Gmagick; getters дают целые значения.

Настроить показ функцией каталогаСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$sequence=new Gmagick();
$sequence->readImageBlob(file_get_contents(__DIR__.'/views.ppm'));
$sequence->setImageIndex(0);
do{$sequence->setImageDelay(30);$sequence->setImageDispose(Gmagick::DISPOSE_NONE);$sequence->setImageIterations(0);}while($sequence->nextImage());
$sequence->setImageIndex(0);
function configureTimeline(Gmagick $sequence,array $delays,int $loops,int $dispose):Gmagick{
    if(count($delays)!==$sequence->getNumberImages()||$loops<0||!in_array($dispose,[1,2,3],true))throw new InvalidArgumentException('Invalid timeline options');
    foreach($delays as $delay)if(!is_int($delay)||$delay<1||$delay>1000)throw new InvalidArgumentException('Delay must be1..1000 centiseconds');
    foreach($delays as $i=>$delay){$sequence->setImageIndex($i);$sequence->setImageDelay($delay);$sequence->setImageDispose($dispose);}
    $sequence->setImageIndex(0);$sequence->setImageIterations($loops);return $sequence;
}
configureTimeline($sequence,[30,30,120],0,Gmagick::DISPOSE_NONE);
$firstIterations=$sequence->getImageIterations();$sequence->setImageIndex(2);$last=[$sequence->getImageDelay(),$sequence->getImageDispose()];
$sequence->setImageDepth(8);
$sequence->setImageFormat('PNG');
$sequence->writeImage(__DIR__.'/gm100-configured-last.png',false);
$sequence->setImageIndex(0);
do{$sequence->setImageFormat('GIF');}while($sequence->nextImage());
$sequence->setImageIndex(0);$sequence->writeImage(__DIR__.'/gm100-configured.gif',true);
$errors=['length'=>gmTry(fn()=>configureTimeline($sequence,[30],0,1)),'type'=>gmTry(fn()=>$sequence->setImageDelay([]))];

gmReport('gm100-time-function',["gmagick.construct", "gmagick.readimageblob", "gmagick.setimageindex", "gmagick.setimagedelay", "gmagick.setimagedispose", "gmagick.setimageiterations", "gmagick.nextimage", "gmagick.getnumberimages", "gmagick.getimageiterations", "gmagick.getimagedelay", "gmagick.getimagedispose", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage"],["gm100-configured-last.png"],
    ['loop_forever'=>$firstIterations===0,'last_settings'=>$last===[120,1],'length_rejected'=>$errors['length']['class']==='InvalidArgumentException','type_rejected'=>$errors['type']['class']==='TypeError','size_gm100_configured_last'=>gmInspect('gm100-configured-last.png')['width']===320&&gmInspect('gm100-configured-last.png')['height']===200],
    $errors ?? [], ['last_delay_dispose'=>$last,'first_iterations'=>$firstIterations]);
echo "gm100-configured-last.png 320x200\n";
Ожидаемый результат
gm100-configured-last.png 320x200

Самостоятельный ZIP: php example.php с Gmagick. Окружение: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные PPM и готовый views.gif восстанавливаются из .b64. gm-fixture.php проверяет файлы и записывает metrics.json; изучаемые операции видны в самой программе. PNG и GIF сохраняются рядом.

Вывод успешного запуска
gm100-configured-last.png 320x200

Если в функцию передан неверный массив, полезнее остановиться до записи файла, чем выпустить наполовину настроенную последовательность. Отдельный TypeError при передаче массива вместо числа показывает границу PHP-типов.

Упражнение: delay25 — сколько миллисекунд? Ответ:250. Меняет ли setImageIterations скорость каждого кадра? Ответ:нет. Почему на полных непрозрачных кадрах dispose1 и2 выглядят одинаково? Ответ:следующий кадр закрывает весь холст. Для частичных кадров нужен учёт прежнего содержимого. Следующий шаг — общая палитра и области изменений.

Практический вопрос и наш ответ

How do remove the flickering in this ImageMagick produced animation. Ive tried exactly as documented: В начале анимации мерцает пустой прозрачный кадр, хотя ему задана нулевая задержка и следующему указан dispose previous. Мы начинаем с содержательного полного кадра, используем положительное время и показываем реальное действие dispose на частичных областях. Ноль не обещает невидимый кадр. Отдельные PNG последнего состояния позволяют увидеть пропавший фон или оставшийся предмет.

Функции и методы этой темы

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

Gmagick::getimagedelayЧитает задержку активного кадра как целое число сотых секунды

PECL gmagick >= Unknown · gmagick

Gmagick::getimagedelay(): int

Читает задержку активного кадра как целое число сотых секунды. Фраза PHP.net о composite operator в разделе возврата ошибочна: getter вызывает MagickGetImageDelay, это время, не оператор наложения.

Gmagick::getimagedelay(): int

Аргументов нет. Getter или навигация относятся к текущему изображению и списку, как описано выше.

Возврат: Целое число с указанным выше смыслом.

PHP.net указывает GmagickException при ошибке. Неверный тип аргумента может быть отклонён PHP как TypeError до выполнения операции. Проверки диапазона в функциях примеров — отдельные правила приложения. Для nextImage/previousImage обычная граница обхода также выражается false.

Доступность: PECL Gmagick; нижняя граница на этих официальных страницах указана Unknown. Это не означает определённую версию PHP. Примеры привязаны к указанной Q16-сборке, ограничения которой разобраны в уроке.

Два применения: Сравнить спокойный и быстрый показ; Задержать последний ракурс.

Gmagick::getimagedisposeЧитает целый код метода удаления активного кадра после его времени показа

PECL gmagick >= Unknown · gmagick

Gmagick::getimagedispose(): int

Читает целый код метода удаления активного кадра после его времени показа. Этот getter не удаляет кадр из списка: для удаления элемента списка служит removeImage.

Gmagick::getimagedispose(): int

Аргументов нет. Getter или навигация относятся к текущему изображению и списку, как описано выше.

Возврат: Целое число с указанным выше смыслом.

PHP.net указывает GmagickException при ошибке. Неверный тип аргумента может быть отклонён PHP как TypeError до выполнения операции. Проверки диапазона в функциях примеров — отдельные правила приложения. Для nextImage/previousImage обычная граница обхода также выражается false.

Доступность: PECL Gmagick; нижняя граница на этих официальных страницах указана Unknown. Это не означает определённую версию PHP. Примеры привязаны к указанной Q16-сборке, ограничения которой разобраны в уроке.

Два применения: Сравнить удаление на полных кадрах; Увидеть влияние удаления на частичные кадры.

Gmagick::getimageiterationsЧитает целое значение iterations активного изображения

PECL gmagick >= Unknown · gmagick

Gmagick::getimageiterations(): int

Читает целое значение iterations активного изображения. Для итогового GIF проверяем настройку первого кадра и прочитанный файл; это другой показатель, чем длительность отдельного кадра.

Gmagick::getimageiterations(): int

Аргументов нет. Getter или навигация относятся к текущему изображению и списку, как описано выше.

Возврат: Целое число с указанным выше смыслом.

PHP.net указывает GmagickException при ошибке. Неверный тип аргумента может быть отклонён PHP как TypeError до выполнения операции. Проверки диапазона в функциях примеров — отдельные правила приложения. Для nextImage/previousImage обычная граница обхода также выражается false.

Доступность: PECL Gmagick; нижняя граница на этих официальных страницах указана Unknown. Это не означает определённую версию PHP. Примеры привязаны к указанной Q16-сборке, ограничения которой разобраны в уроке.

Два применения: Сравнить непрерывный цикл и конечный повтор; Настроить показ функцией каталога.

Gmagick::setimagedelayЗадаёт время показа активного кадра до перехода к следующему

PECL gmagick >= Unknown · gmagick

Gmagick::setimagedelay(int $delay): Gmagick

Задаёт время показа активного кадра до перехода к следующему. Настройка относится к одному кадру, поэтому одинаковую скорость всей последовательности задают явным обходом. Для длинной паузы в конце меняют именно последний кадр.

Gmagick::setimagedelay(int $delay): Gmagick
АргументТипСмысл
delayint — обязательныйЗадержка текущего кадра в сотых долях секунды:30 означает0.30s, в файле GIF это300ms. Ноль не обещает мгновенного показа в браузере.

Возврат: Изменённый Gmagick; объект можно использовать для следующей операции.

PHP.net указывает GmagickException при ошибке. Неверный тип аргумента может быть отклонён PHP как TypeError до выполнения операции. Проверки диапазона в функциях примеров — отдельные правила приложения. Для nextImage/previousImage обычная граница обхода также выражается false.

Доступность: PECL Gmagick; нижняя граница на этих официальных страницах указана Unknown. Это не означает определённую версию PHP. Примеры привязаны к указанной Q16-сборке, ограничения которой разобраны в уроке.

Два применения: Сравнить спокойный и быстрый показ; Задержать последний ракурс.

Gmagick::setimagedisposeЗадаёт действие после показа кадра: оставить область, очистить её фоном или восстановить прежнее содержимое

PECL gmagick >= Unknown · gmagick

Gmagick::setimagedispose(int $disposeType): Gmagick

Задаёт действие после показа кадра: оставить область, очистить её фоном или восстановить прежнее содержимое. Для полных непрозрачных кадров разница может быть невидима; при частичных кадрах настройка влияет на собранную картинку.

Gmagick::setimagedispose(int $disposeType): Gmagick
АргументТипСмысл
disposeTypeint — обязательныйЦелое значение метода удаления:0 не определён,1 оставить,2 восстановить фон области,3 восстановить предыдущее содержимое области. В коде используем именованные DISPOSE_*;4…7 зарезервированы GIF89a.

Возврат: Изменённый Gmagick; объект можно использовать для следующей операции.

PHP.net указывает GmagickException при ошибке. Неверный тип аргумента может быть отклонён PHP как TypeError до выполнения операции. Проверки диапазона в функциях примеров — отдельные правила приложения. Для nextImage/previousImage обычная граница обхода также выражается false.

Доступность: PECL Gmagick; нижняя граница на этих официальных страницах указана Unknown. Это не означает определённую версию PHP. Примеры привязаны к указанной Q16-сборке, ограничения которой разобраны в уроке.

Два применения: Сравнить удаление на полных кадрах; Увидеть влияние удаления на частичные кадры.

Gmagick::setimageiterationsЗадаёт iterations изображения, используемые при записи повторяющейся анимации

PECL gmagick >= Unknown · gmagick

Gmagick::setimageiterations(int $iterations): Gmagick

Задаёт iterations изображения, используемые при записи повторяющейся анимации. Настройка всего цикла и задержки кадров решают разные задачи. Не используйте ошибочную единицу1/100s из описания этого параметра PHP.net.

Gmagick::setimageiterations(int $iterations): Gmagick
АргументТипСмысл
iterationsint — обязательныйЧисло повторений анимации. Ноль означает непрерывное повторение. Это не задержка кадра: строка о1/100s на странице этого параметра — ошибка документации. Хранимое расширение GIF и getter сравниваются отдельно.

Возврат: Изменённый Gmagick; объект можно использовать для следующей операции.

PHP.net указывает GmagickException при ошибке. Неверный тип аргумента может быть отклонён PHP как TypeError до выполнения операции. Проверки диапазона в функциях примеров — отдельные правила приложения. Для nextImage/previousImage обычная граница обхода также выражается false.

Доступность: PECL Gmagick; нижняя граница на этих официальных страницах указана Unknown. Это не означает определённую версию PHP. Примеры привязаны к указанной Q16-сборке, ограничения которой разобраны в уроке.

Два применения: Сравнить непрерывный цикл и конечный повтор; Настроить показ функцией каталога.

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.