blog-RuinDig

短かったり長かったりする。Blog posts are my own.

画像比較スライダーのCocoenをブログに導入した

はてなブログで以下のブログ記事を作るにあたって、画像比較スライダーのCocoenを導入した。

ruindig.hatenablog.jp

試行錯誤

Visual Studio Codeを使いながら試行錯誤した。なお、このブログではMarkdown記法(12)を使用している。

code.visualstudio.com

Before/Afterのラベルを貼れるのが良いと思って、最初はBeerSliderを試したが、Safariで動かないと教えてもらったので止めた。

indoor-days.blogspot.com

github.com

次に試したのがCocoenだった。

taisho-goes.hatenablog.com

github.com

CocoenはGitHubよりも紹介しているブログ記事の方が分かりやすかった。

JQuery-Images-Compareとbefore-after.jsも試したが、上手く行かなかったので止めた。

github.com

github.com

Cocoenを導入する

以下、Cocoenを導入するために実装したJavaScript、CSS、HTMLコードになる。

JavaScript

はてなブログの管理画面の「デザイン→カスタマイズ→フッタ」に以下のJavaScriptのコードを貼り付けた。コードの内容はCocoenを紹介したブログ記事と同じである。

<script>
!function(e) {
if ("object" == typeof exports && "undefined" != typeof module)
module.exports = e();
else if ("function" == typeof define && define.amd)
define([], e);
else {
var t;
t = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof self ? self : this, t.Cocoen = e()
}
}(function() {
return function e(t, n, i) {
function s(o, a) {
if (!n[o]) {
if (!t[o]) {
var l = "function" == typeof require && require;
if (!a && l)
return l(o, !0);
if (r)
return r(o, !0);
var d = new Error("Cannot find module '" + o + "'");
throw d.code = "MODULE_NOT_FOUND", d
}
var h = n[o] = {
exports: {}
};
t[o][0].call(h.exports, function(e) {
var n = t[o][1][e];
return s(n ? n : e)
}, h, h.exports, e, t, n, i)
}
return n[o].exports
}
for (var r = "function" == typeof require && require, o = 0; o < i.length; o++)
s(i[o]);
return s
}({
1: [function(e, t, n) {
"use strict";
function i(e, t) {
if (!(e instanceof t))
throw new TypeError("Cannot call a class as a function")
}
var s = Object.assign || function(e) {
for (var t = 1; t < arguments.length; t++) {
var n = arguments[t];
for (var i in n)
Object.prototype.hasOwnProperty.call(n, i) && (e[i] = n[i])
}
return e
},
r = function() {
function e(e, t) {
for (var n = 0; n < t.length; n++) {
var i = t[n];
i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i)
}
}
return function(t, n, i) {
return n && e(t.prototype, n), i && e(t, i), t
}
}(),
o = function() {
function e(t, n) {
i(this, e), this.options = s({}, e.defaults, n), this.element = t || document.querySelector(".cocoen"), this.init()
}
return r(e, [{
key: "init",
value: function() {
this.createElements(), this.addEventListeners(), this.dimensions()
}
}, {
key: "createElements",
value: function() {
var e = document.createElement("span");
e.className = this.options.dragElementSelector.replace(".", ""), this.element.appendChild(e)
var t = document.createElement("div"),
n = this.element.querySelector("img:first-child");
t.appendChild(n.cloneNode(!0)), n.parentNode.replaceChild(t, n), this.dragElement = this.element.querySelector(this.options.dragElementSelector), this.beforeElement = this.element.querySelector("div:first-child"), this.beforeImage = this.beforeElement.querySelector("img")
}
}, {
key: "addEventListeners",
value: function() {
 this.element.addEventListener("click", this.onTap.bind(this)), this.element.addEventListener("mousemove", this.onDrag.bind(this)), this.element.addEventListener("touchmove", this.onDrag.bind(this)), this.dragElement.addEventListener("mousedown", this.onDragStart.bind(this)), this.dragElement.addEventListener("touchstart", this.onDragStart.bind(this)), window.addEventListener("mouseup", this.onDragEnd.bind(this)), window.addEventListener("resize", this.dimensions.bind(this))
}
}, {
key: "dimensions",
value: function() {
this.elementWidth = parseInt(window.getComputedStyle(this.element).width, 10), this.elementOffsetLeft = this.element.getBoundingClientRect().left + document.body.scrollLeft, this.beforeImage.style.width = this.elementWidth + "px", this.dragElementWidth = parseInt(window.getComputedStyle(this.dragElement).width, 10), this.minLeftPos = this.elementOffsetLeft + 10, this.maxLeftPos = this.elementOffsetLeft + this.elementWidth - this.dragElementWidth - 10
}
}, {
key: "onTap",
value: function(e) {
e.preventDefault(), this.leftPos = e.pageX ? e.pageX : e.touches[0].pageX, this.requestDrag()
}
}, {
key: "onDragStart",
value: function(e) {
e.preventDefault();
var t = e.pageX ? e.pageX : e.touches[0].pageX,
n = this.dragElement.getBoundingClientRect().left + document.body.scrollLeft;
this.posX = n + this.dragElementWidth - t, this.isDragging = !0
}
}, {
key: "onDragEnd",
value: function(e) {
e.preventDefault(), this.isDragging = !1
}
}, {
key: "onDrag",
value: function(e) {
e.preventDefault(), this.isDragging && (this.moveX = e.pageX ? e.pageX : e.touches[0].pageX, this.leftPos = this.moveX + this.posX - this.dragElementWidth, this.requestDrag())
}
}, {
key: "drag",
value: function() {
this.leftPos < this.minLeftPos ? this.leftPos = this.minLeftPos : this.leftPos > this.maxLeftPos && (this.leftPos = this.maxLeftPos);
var e = this.leftPos + this.dragElementWidth / 2 - this.elementOffsetLeft;
e /= this.elementWidth;
var t = 100 * e + "%";
this.dragElement.style.left = t, this.beforeElement.style.width = t, this.options.dragCallback && this.options.dragCallback(e)
}
}, {
key: "requestDrag",
value: function() {
window.requestAnimationFrame(this.drag.bind(this))
}
}]), e
}();
o.defaults = {
dragElementSelector: ".cocoen-drag",
dragCallback: null
}, t.exports = o
}, {}]
}, {}, [1])(1)
});
</script>
<script>
document.querySelectorAll('.cocoen').forEach(function(element){
new Cocoen(element);
});
/* The MIT License (MIT) */
/* Copyright (c) 2015 Koen Romers */
/* https://github.com/koenoe/cocoen/blob/main/LICENSE */
</script>

CSS

はてなブログの管理画面の「デザイン→カスタマイズ→デザインCSS」に以下のCSSのコードを貼り付けた。

.cocoen-drag::before.cocoen-drag::afterに分けてborderプロパティを調整して左右の外側を向く三角形を2つ作る事で、左右にスライドして動かすというのを分かりやすくしたつもりである。borderプロパティを調整するために以下の解説を参考にした。

www.granfairs.com

.cocoen {
    box-sizing: border-box;
    cursor: pointer;
    line-height: 0;
    margin: 0;
    overflow: hidden;
    padding: 0;
    position: relative;
    -webkit-user-select: text;
    -moz-user-select: text;
    -ms-user-select: text;
    user-select: text;
}
.cocoen * {
    box-sizing: inherit;
}
.cocoen ::after, .cocoen ::before {
    box-sizing: inherit;
}
.cocoen img, .cocoen picture > img {
    max-width: none;
}
.cocoen > img, .cocoen > picture > img {
    display: block;
    width: 100%;
}
.cocoen > div:first-child, picture .cocoen > div {
    height: 100%;
    left: 0;
    overflow: hidden;
    position: absolute;
    top: 0;
    width:50%;
}
.cocoen-drag {
    background: #fff;
    bottom: 0;
    cursor: ew-resize;
    left: 50%;
    margin-left: 0px;
    position: absolute;
    top: 0;
    width:2px;
}
  /* 左向きの三角形 */
.cocoen-drag::before {
    border-top: 18px solid transparent;
    border-bottom: 18px solid transparent;
    border-right: 14px solid #fff;
    content: '';
    height: 30px;
    right: 10%;
    margin-right: 6px;
    margin-top: -18px;
    position: absolute;
    top: 50%;
    width: 8px;
}
  /* 右向きの三角形 */
.cocoen-drag::after {
    border-top: 18px solid transparent;
    border-bottom: 18px solid transparent;
    border-left: 14px solid #fff;
    content: '';
    height: 30px;
    left: 10%;
    margin-left: 6px;
    margin-top: -18px;
    position: absolute;
    top: 50%;
    width: 8px;
/* The MIT License (MIT) */
/* Copyright (c) 2015 Koen Romers */
/* https://github.com/koenoe/cocoen/blob/main/LICENSE */
}

ブログ記事内の本文内HTML

はてなフォトライフの個別の画像URLを右クリックメニューでコピーして<img src="">に貼り付ける。

<!--はてなブログのMarkdown記法で使う場合-->
<figure class="figure-image figure-image-fotolife">
<div class="cocoen">
<img src="<!--左側の画像URL-->">
<img src="<!--右側の画像URL-->">
</div>
<figcaption><!--画像キャプションは任意、無くても良い--></figcaption>
</figure>

Cocoenに限らず、はてな記法でブログの本文にHTMLコードを挿入する時は> <の間にHTMLコードを挟んで入力する必要がある。

<!--はてなブログのはてな記法で使う場合-->
><figure class="figure-image figure-image-fotolife">
<div class="cocoen">
<img src="<!--左側の画像URL-->">
<img src="<!--右側の画像URL-->">
</div>
<figcaption><!--画像キャプションは任意、無くても良い--></figcaption>
</figure><

GitHub Pagesライブドアブログ、GoogleのブログサービスのBloggerAmebaブログなど、はてなブログ以外で使う場合は以下の通り。

<!--GitHub PagesやBloggerなどで使う場合-->
<html>
<head>
<body>
<figure>
<div class="cocoen">
<img src="<!--左側の画像URL-->">
<img src="<!--右側の画像URL-->">
</div>
<figcaption><!--画像キャプションは任意、無くても良い--></figcaption>
</figure>
</body>
</head>
</html>
<!--GitHub PagesやBloggerなどで使う場合-->
<html>
<head>
<body>
<p>
<div class="cocoen">
<img src="<!--左側の画像URL-->">
<img src="<!--右側の画像URL-->">
</div>
<figcaption><!--画像キャプションは任意、無くても良い--></figcaption>
</p>
</body>
</head>
</html>

大きさの違う画像での比較

片方が小さい画像を用いて、大きさの違う画像を比較する場合は、小さい方の画像に<div style="background: white; display: flex; align-items: center;"></div>を適用して上下の位置を中央に寄せて小さい画像の背後に大きい画像が写り込まないように背景を白くする事ができる。

<figure>
<div class="cocoen">
<div style="background: white; display: flex; align-items: center;"> <!--小さい方の画像に白い背景と上下中央寄せを適用する-->
<img src="<!--小さい方の画像URL-->">
</div>
<img src="<!--大きい方の画像URL-->">
</div>
<figcaption><!--画像キャプションは任意、無くても良い--></figcaption>
</figure>

カメラを縦向きにして撮った画像(縦の画像)とカメラを横向きにして撮った画像(横の画像)での比較は、縦の長さや横の長さを調整するなどしたが、縦の画像が横の画像に合わせようと拡大されてしまい、試した限りでは適していない事が分かった。

 <!--上手く行かなかったパターン-->
 <figure>
 <div class="cocoen">
 <div style="background: white; width: 60; height: 100; display: flex; align-items: center;"> <!--縦の画像のサイズ調整-->
 <img src="<!--縦の画像-->">
 </div>
 <img src="<!--横の画像-->">
 </div>
 <figcaption><!--画像キャプションは任意、無くても良い--></figcaption>
 </figure>

実装例

2022年10月10日、千葉ポートアリーナで開催された大相撲千葉場所での様子。左は行司の式守伊之助、右は貴景勝関と正代関の取り組み。(CC-BY-4.0 - RuinDig/Yuki Uchida)

千葉県の鋸南町と富津市の境にある鋸山に立つ百尺観音の頭部 千葉県の鋸南町と富津市の境にある鋸山に立つ百尺観音の足元

千葉県の鋸南町と富津市の境にある鋸山に立つ百尺観音。左は頭部、右は足元。(CC-BY-4.0 - RuinDig/Yuki Uchida)

千葉県成田市にあるうなぎ専門店「川豊本店」のうな重 千葉県成田市にあるうなぎ専門店「川豊本店」のうなぎの骨せんべいと鯉の洗い(鯉の刺身)

千葉県成田市にあるうなぎ専門店「川豊本店」で、左はうな重、右はうなぎの骨せんべいと鯉の洗い(鯉の刺身)。(CC-BY-4.0 - RuinDig/Yuki Uchida)

神奈川県横浜市にある「グルメ廻転寿司 まぐろ問屋 三浦三崎港」の横浜ワールドポーターズ店での本マグロの頭身大トロ 神奈川県横浜市にある「グルメ廻転寿司 まぐろ問屋 三浦三崎港」の横浜ワールドポーターズ店でのむじょか生サバ

神奈川県横浜市にある「グルメ廻転寿司 まぐろ問屋 三浦三崎港」の横浜ワールドポーターズ店で、左は本マグロの頭身大トロ、右はむじょか生サバ。(CC-BY-4.0 - RuinDig/Yuki Uchida)

【片方が小さい画像を用いて、大きさの違う画像を比較する場合】

東京都千代田区丸の内にある極上中華そば 福味 KITTE丸の内店の紅の煮干しラーメン
東京都豊島区巣鴨にある洋食小林のスコッチエッグ

左:東京都千代田区丸の内にある極上中華そば 福味 KITTE丸の内店の紅の煮干しラーメン、右:東京都豊島区巣鴨にある洋食小林のスコッチエッグ(CC-BY-4.0 - RuinDig/Yuki Uchida)

【縦の画像と横の画像を用いた比較】

押上駅前自転車駐車場の屋上広場から見た東京スカイツリー
東京スカイツリーの天望デッキから見える東京タワー

左:押上駅前自転車駐車場の屋上広場から見た東京スカイツリー、右:東京スカイツリーの天望デッキから見える東京タワー。左は東京スカイツリーの全体像が収まっている画像だが見切れてしまっている。(CC-BY-4.0 - RuinDig/Yuki Uchida)


-end-