SCORE: 0

SVGで作る

風船の様なページ背景 ゲーム風仕上げ

ウェブサイトトップページの背景などに。ゲームの様な体験を追加。

メインコード:
/**
 * バブルの様なSVG画像出力
 *
 * @author ao-system, Inc.
 * @date 2024-02-04
 */

export class BubbleImage {
	'use strict';
	#transform = 20;
	#points = Array.from({length:8}, () => Math.random() * (this.#transform * 2) - this.#transform);
	#offsets = Array.from({length:8}, () => 0.2 + Math.random() * 0.2);	//speed
	#scaleDirection = 0;	//0:なし -1:小さくなる 1:大きくなる
	#scale = 1;
	#color = ['rgba(255,255,255,0.2)','rgba(150,200,200,0.5)'];
	#filter = 'blur(3px) drop-shadow(0 0px 3px rgba(255,255,255,0.5))';
	#svgElm;
	#svgPath;
	#animationId;
	constructor(param) {
		if (param.color) {
			this.#color = param.color;
		}
		if (param.filter) {
			this.#filter = param.filter;
		}
		this.#init();
	}
	set color(val) {
		this.#color = val;
		this.#createSvg();
	}
	set filter(val) {
		this.#filter = val;
		this.#createSvg();
	}
	get svg() {
		return this.#svgElm;
	}
	dispose() {
		cancelAnimationFrame(this.#animationId);
		const bomb = this.#svgElm.classList.contains('bomb');
		this.#svgElm = undefined;
		return bomb;
	}
	#init() {
		this.#createSvg();
		this.#animateSvg();
	}
	//<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200px" height="200px" viewBox="0 0 200 200">
	//	<radialGradient id="rg" cx="100" cy="100" r="50%" gradientUnits="userSpaceOnUse">
	//		<stop offset="0" style="stop-color:#7f6"/>
	//		<stop offset="1" style="stop-color:#ff9"/>
	//	</radialGradient>
	//	<path fill="url(#rg)" d="M150,100C150,127,127,150,100,150S50,127,50,100S72,50,100,50S150,72,150,100z"/>
	//</svg>
	#createSvg() {
		const radialGradientId = this.#generateRandomId();
		this.#svgElm = document.createElementNS('http://www.w3.org/2000/svg','svg');
		this.#svgElm.setAttribute('xmlns','http://www.w3.org/2000/svg');
		//this.#svgElm.setAttribute('width','200px');
		//this.#svgElm.setAttribute('height','200px');
		this.#svgElm.setAttribute('viewBox','0 0 200 200');
		this.#svgElm.style.filter = this.#filter;
		this.#svgElm.style.transform = 'scale(' + this.#scale + ')';
		const radialGradient = document.createElementNS('http://www.w3.org/2000/svg','radialGradient');
		radialGradient.setAttribute('id',radialGradientId);
		radialGradient.setAttribute('cx',100);
		radialGradient.setAttribute('cy',100);
		radialGradient.setAttribute('r','50%');
		radialGradient.setAttribute('gradientUnits','userSpaceOnUse');
		const stop1 = document.createElementNS('http://www.w3.org/2000/svg','stop');
		stop1.setAttribute('offset',0);
		stop1.setAttribute('style','stop-color:' + this.#color[0]);
		const stop2 = document.createElementNS('http://www.w3.org/2000/svg','stop');
		stop2.setAttribute('offset',1);
		stop2.setAttribute('style','stop-color:' + this.#color[1]);
		this.#svgPath = document.createElementNS('http://www.w3.org/2000/svg','path');
		this.#svgPath.setAttribute('fill','url(#' + radialGradientId + ')');
		radialGradient.appendChild(stop1);
		radialGradient.appendChild(stop2);
		this.#svgElm.appendChild(radialGradient);
		this.#svgElm.appendChild(this.#svgPath);
		this.#svgElm.addEventListener('mouseover', () => {
			this.#svgElm.classList.add('bomb');
			stop1.setAttribute('style','stop-color:' + '#f00');
			if (this.#scaleDirection == 0) {
				this.#scaleDirection = -1;
			}
		});
	}
	#generateRandomId(idLength = 12) {
		const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
		return Array.from({length:idLength}, () => characters[Math.floor(Math.random() * characters.length)]).join('');
	}
	#animateSvg() {
		if (typeof this.#svgElm == 'undefined') {
			return;
		}
		if (this.#scaleDirection == -1) {
			this.#scale -= 0.05;
			this.#svgElm.style.transform = 'scale(' + this.#scale + ')';
			if (this.#scale < 0.5) {
				this.#scaleDirection = 1;
			}
		} else if (this.#scaleDirection == 1) {
			this.#scale += 0.005;
			this.#svgElm.style.transform = 'scale(' + this.#scale + ')';
			if (this.#scale >= 1) {
				this.#scaleDirection = 0;
			}
		}
		//
		for (let i = 0; i < this.#points.length; i++) {
			this.#points[i] += this.#offsets[i];
			if (this.#points[i] > this.#transform || this.#points[i] < -this.#transform) {
				this.#offsets[i] *= -1;
			}
		}
		const d = [
			`M${150 + this.#points[0]},${100 + this.#points[1]}C${150 + this.#points[0]},${127 + this.#points[1]},${127 + this.#points[2]},${150 + this.#points[3]},${100 + this.#points[2]},${150 + this.#points[3]}`,
			`S${50 + this.#points[4]},${127 + this.#points[5]},${50 + this.#points[4]},${100 + this.#points[5]}`,
			`S${72 + this.#points[6]},${50 + this.#points[7]},${100 + this.#points[6]},${50 + this.#points[7]}`,
			`S${150 + this.#points[0]},${72 + this.#points[1]},${150 + this.#points[0]},${100 + this.#points[1]}`,
			`z`,
		].join('');
		this.#svgPath.setAttribute('d',d);
		this.#animationId = requestAnimationFrame(() => this.#animateSvg());
	}
}
スマートフォンなど非力なコンピュータでは処理が間に合わない場合が有ります。
処理性能に合わせて描画処理を切り分けるなど必要になります。
SVGとCSSを工夫すれば多彩な表現が可能。それをJavaScriptでアニメ―トする。
工夫次第で面白い表現が可能。サイトデザインやコンセプトに合わせて目を引く背景画像を作成しよう。
ゲームの様な体験を追加することで、ページ滞在時間や再来回数が大幅に伸びることが期待できるかも。
この記事は2024年2月当時の物です。
このサイトについてのお問い合わせはエーオーシステムまでお願いいたします。
ご使用上の過失の有無を問わず、本プログラムの運用において発生した損害に対するいかなる請求があったとしても、その責任を負うものではありません。