'Clockwise', 'ccw' => 'Counterclockwise', ]; private const SPIN_MIN = 0.01; private const SPIN_MAX = 1000; private $imageUpload = null; private $spin = false; private $spinSpeed = 1; private $spinDirection = 'cw'; private $syncWithAudio = false; public function getEffectName(): string { return 'Foreground Image'; } public function getEffectProperties(): array { return [ [ 'name' => 'img', 'title' => 'Image', 'type' => [ 'name' => 'upload', 'allowed' => self::IMG_MIME, ], ], [ 'name' => 'spin', 'title' => 'Spin Animation', 'default' => false, 'type' => [ 'name' => 'bool', ], ], [ 'name' => 'spnspd', 'title' => 'Spin Animation Period', 'default' => 1, 'type' => [ 'name' => 'float', 'min' => self::SPIN_MIN, 'max' => self::SPIN_MAX, ], ], [ 'name' => 'spndir', 'title' => 'Spin Animation Direction', 'default' => 'br', 'type' => [ 'name' => 'select', 'options' => self::SPIN_DIRS, ], ], [ 'name' => 'sync', 'title' => 'Synchronise with Background Audio', 'default' => false, 'type' => [ 'name' => 'bool', ], ], ]; } public function setEffectParams(array $vars, bool $quiet = false): void { try { if(isset($vars['img']) && is_string($vars['img'])) { try { $this->imageUpload = Upload::byId($vars['img']); } catch(Exception $ex) { if(!$quiet) throw $ex; } if(!$quiet && !in_array($this->imageUpload->getType(), self::IMG_MIME)) throw new PageEffectException('Image upload was of invalid type.'); } } catch(UploadNotFoundException $ex) { $this->imageUpload = null; } if(isset($vars['spin'])) $this->spin = is_bool($vars['spin']) ? $vars['spin'] : (is_string($vars['spin']) ? boolval($vars['spin']) : false); if(isset($vars['sync'])) $this->syncWithAudio = is_bool($vars['sync']) ? $vars['sync'] : (is_string($vars['sync']) ? boolval($vars['sync']) : false); if(isset($vars['spnspd'])) { $this->spinSpeed = is_int($vars['spnspd']) || is_float($vars['spnspd']) ? $vars['spnspd'] : (is_string($vars['spnspd']) ? floatval($vars['spnspd']) : 1); if(!$quiet && ($this->spinSpeed < self::SPIN_MIN || $this->spinSpeed > self::SPIN_MAX)) throw new PageEffectException(sprintf('Spin speed may not be less than %d or more than %d', self::SPIN_MIN, self::SPIN_MAX)); } if(isset($vars['spndir']) && is_string($vars['spndir']) && array_key_exists($vars['spndir'], self::SPIN_DIRS)) $this->spinDirection = $vars['spndir']; } public function getEffectParams(): array { return [ 'img' => empty($this->imageUpload) ? null : $this->imageUpload->getId(), 'spin' => $this->spin, 'spnspd' => $this->spinSpeed, 'spndir' => $this->spinDirection, 'sync' => $this->syncWithAudio, ]; } public function applyEffect(PageBuilder $builder): void { $element = $builder->getContainer()->appendChild(new HtmlTag('div', ['class' => 'ForegroundImage'])); $imageTarget = $element->appendChild(new HtmlTag('div', ['class' => 'ForegroundImage_Image', 'id' => 'ForegroundImage'])); if(!empty($this->imageUpload)) { $imageSize = getimagesize($this->imageUpload->getPath()); if($imageSize !== false) { $styleText = sprintf('width: %dpx; height: %dpx', $imageSize[0], $imageSize[1]); if(!empty($_GET['preview']) || !$this->syncWithAudio) $styleText .= sprintf(';background-image:url(\'%s\')', $this->imageUpload->getUrl()); if(empty($_GET['preview']) && $this->spin) $styleText .= sprintf(';animation: SharedAnimation_Spin360 infinite linear %s %Fs', $this->spinDirection === 'cw' ? 'normal' : 'reverse', $this->spinSpeed); $imageTarget->setAttribute('style', $styleText); $scriptText = 'window.addEventListener(\'DOMContentLoaded\', function() {'; $scriptText .= 'synchroniseBackgroundWithAudio(\'' . $this->imageUpload->getUrl() . '\', \'ForegroundImage\');'; $scriptText .= '});'; $scriptTag = new HtmlTag('script', ['type' => 'text/javascript']); $scriptTag->setTextContent($scriptText); $builder->getHead()->appendChild($scriptTag); } } } }