キャンバス画像分割効果の実装
cssdesignawardsを見ていたら、画像の中身を分割するエフェクト(URL https://weareludwig.com をクリックすると、その中を見ることができ、とてもクールな感じがするので、自分でも実装してみましたが、なかなかうまくいきました。エフェクトを見る https://codepen.io/geeknoble/pen/OQaOVG
分析
まず、画像の内容が小さな矩形に分割され、それぞれの矩形がランダムにパンされていることがわかります。CanvasのdrawImage関数は画像の内容を切り抜いてCanvasのキャンバスに描くことができるので、この効果の大原則はdrawImageを使うことです。画像の内容を中断して復元する効果と、次の画像と切り替える効果の2つがあり、どちらもdrawImageが使えるが移動距離が違うということです。大まかな考え方はあるので、先に実装してしまうのも手です。
初期作業
まず、画像の幅や高さ、矩形の数、クリッピングの大きさなど、いくつかの変数を初期化します。次に、各矩形の座標を計算し、双方向ループを使用して矩形の座標をデータに保存します。各矩形はランダムな変位を持ちますが,これも保存してランダムで保存する必要があります.ここで、x,y はキャンバスの座標、x1,y1 は画像の切り出しの座標です。
init: function (context, width, height, area, img) {
this.context = context;
this.img = img;
this.imgWidth = img[0].width; //image width and height
this.imgHeight = img[0].height;
this.index = 0; //the current image serial number
this.width = width; //Canvas width and height
this.height = height;
this.area = height/12; //Small rectangle length
this.countX = width / this.area; //number of small rectangles horizontally and vertically
this.countY = height / this.area;
this.wx = this.imgWidth / this.countX; // the width and height of the image in the small rectangle
this.wy = this.imgHeight / this.countY;
this.state = true; //State of the image, true means not split
this.dataFlag = true; //state of the small rectangle, true means no random value added
this.duration = 1000; //animation time
this.duration2 = 1500;
this.startTime = 0;
this.data = []; //the coordinates of the small rectangle
this.randoms = []; //position random values
//initialize the rectangle coordinates
var x1 = 0, y1 = 0, x = 0, y = 0;
for (var i = 0; i < this.countY; i++) {
for (var j = 0; j < this.countX; j++) {
context.drawImage(this.img[this.index], x1, y1, this.wx, this.wy, x, y, this.area, this.area);
//store the rectangle coordinates
this.data.push({
x1: x1,
y1: y1,
x: x,
y: y
});
//add random values
this.randoms.push(random(-this.area, this.area));
x1 += this.wx;
x += this.area;
}
x1 = 0;
y1 += this.wy;
x = 0;
y += this.area;
}
this.checkMargin();
}
エッジの検出
矩形に変位を加える前に、変位後の座標が画像の境界を超えるかどうかを判断する必要があります。たとえば、上部の矩形は y 軸方向にずらした場合のみ上方に移動でき、その条件は、現在の座標に変位値を加えたものが画像の幅と高さを 0 よりも小さいか大きいかどうかになります。更新された座標が0より小さい場合は、乱数値は負でなければならないので正の数に変更する必要があり、画像の高さより大きい場合は負の数に変更すればよい。各矩形は一方向に移動するので、ここではX軸を偶数位置、Y軸を奇数位置に移動するように書いています。
//Detect edges
checkMargin: function () {
var self = this;
this.data.forEach(function (item, index) {
if (index % 2 == 0) { // move the x-axis if the subscript is a multiple of 2, otherwise move the y-axis
if ( item.x1 + self.randoms[index] < 0)
// change to a positive number
self.randoms[index] = -self.randoms[index];
if (item.x1 + self.wx + self.randoms[index] > self.imgWidth )
// change to a negative number
self.randoms[index] = -Math.abs(self.randoms[index])
} else {
if (item.y1 + self.randoms[index] < 0)
self.randoms[index] = -self.randoms[index];
if (item.y1 + self.randoms[index] + self.wy > self.imgHeight)
self.randoms[index] = -Math.abs(self.randoms[index])
}
})
}
分離・復元
アニメーションの内容の分離と復元は、矩形座標の値を更新し、データ中の座標にランダムな値を加えて内容を乱し、ランダムな値を減らして復元することである。
//Detect edges
checkMargin: function () {
var self = this;
this.data.forEach(function (item, index) {
if (index % 2 == 0) { // move the x-axis if the subscript is a multiple of 2, otherwise move the y-axis
if ( item.x1 + self.randoms[index] < 0)
// change to a positive number
self.randoms[index] = -self.randoms[index];
if (item.x1 + self.wx + self.randoms[index] > self.imgWidth )
// change to a negative number
self.randoms[index] = -Math.abs(self.randoms[index])
} else {
if (item.y1 + self.randoms[index] < 0)
self.randoms[index] = -self.randoms[index];
if (item.y1 + self.randoms[index] + self.wy > self.imgHeight)
self.randoms[index] = -Math.abs(self.randoms[index])
}
})
}
座標を格納した後、あなたはパンアニメーションを実装するために行くことができます、移動のプロセスは、滑らかな遷移を持って、我々はTween.jsジョギングアルゴリズムを使用することができます、アルゴリズムは4つのパラメータが現在の時刻、初期位置、終了位置、アニメーション時間です。詳細については、Xinxu Zhang氏のこの記事を参照してください。 https://www.zhangxinxu.com/wordpress/2016/12/how-use-tween-js-animation-easing/ . Tween.jsを使えば、フレームごとに移動距離を把握し、requestAnimationFrameで座標を更新することができます。
blockAnimation: function () {
var flag = 1;
if (this.state) { // Determine if the image is broken or restored
this.update(true)
} else {
flag = -1;
this.update(false);
}
var self = this;
this.startTime = +new Date(); // get the current time
this.state = !this.state;
(function animation() {
var t = +new Date();
if (t >= self.startTime + self.duration) { // end of animation condition
return false;
}
self.data.forEach(function (item, index) {
if (index % 2 == 0) {
var pos = Math.tween.Expo.easeInOut(t - self.startTime, 0, self.randoms[index] * flag, self.duration); // Calculate the distance moved per frame
self.context.drawImage(self.img[self.index], item.x1 + pos, item.y1, self.wx, self.wy, item.x, item.y, self.area, self.area);
} else {
var pos = Math.tween.Expo.easeInOut(t - self.startTime, 0, self.randoms[index] * flag, self.duration);
self.context.drawImage(self.img[self.index], item.x1, item.y1 + pos, self.wx, self.wy, item.x, item.y, self.area, self.area);
}
});
requestAnimationFrame(animation);
})();
}
この時点で、分離・回収のアニメーションが実装されています
画像トグル
次に画像切り替えの部分ですが、回転する画像のアニメーションは、各画像の位置を可視ウィンドウの幅方向に距離を移動させますが、ここも同じです。回転画像と違って、ここではcanvasタグしかないので、切り替え時に現在の画像と次の画像の座標を変更するだけで、現在の画像はy1 + posの距離、次の画像はy1 + pos - imgHeightの距離に移動します(なぜimgHeightを引く必要があるのかは説明不要です)。
// Vertical slide animation
verticalAnimation: function (val) {
if (!this.time2) {
return false;
}
this.checkTime(2);
var self = this;
val ? val = 1 : val = -1; // determine whether to slide up or down
if ((this.index + val) < 0 || (this.index + val) >= (this.img.length)) { //judge if the image number is at the bottom
return false;
}
this.state ? this.update(true) : this.update(false);
this.startTime = +new Date();
(function animation() {
var t = +new Date();
if (t >= self.startTime + self.duration2) {
val === 1 ? self.index++ : self.index--; //adjust the order of the images
self.index < 0 ? self.index = self.img.length - 1 : self.index;
self.index >= self.img.length ? self.index = 0 : self.index;
return false;
}
self.data.forEach(function (item) {
var pos = Math.tween.Cubic.easeInOut(t - self.startTime, 0, (self.imgHeight) * val, self.duration2);
// Update the current image coordinates
self.context.drawImage(self.img[self.index], item.x1, item.y1 + pos, self.wx, self.wy, item.x, item.y, self.area, self.area);
// Update the next image coordinates
self.context.drawImage(self.img[self.index + val], item.x1, item.y1 + pos - self.imgHeight * val, self.wx, self.wy, item.x, item.y, self.area, self.area);
});
requestAnimationFrame(animation);
})()
}
X軸のトグルも同様です。これですべての機能がほぼ完成し、全コードはコードペンで見ることができます。
以上が今回の記事の内容ですが、皆様の学習の一助となり、スクリプトハウスをより一層応援していただければ幸いです。
関連
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
HTMLベースの10秒淘宝網のページを作るために
-
画像のウォーターフォールレイアウトを実現するHTML+CSS+JSのサンプルコード
-
HTML5 ドラッグ&ドロップによるファイルアップロードのサンプルコード
-
html5対応ブラウザの確認方法
-
Html5ネイティブのドラッグ&ドロップ関連イベント紹介と基本的な実装方法
-
データストレージの3つの方法、Cookie sessionstorage localstorageの類似点と相違点の分析
-
html5でtype="range "属性のスライダー機能を実装する。
-
QRコードポスター付きキャンバス生成の落とし穴記録
-
モバイルウェブの画像プリロード方式について簡単に紹介します。
-
canvasで心電図を描画するサンプルコード