追従するフッターをcanvas領域の下まで下げたい。

投稿者: Anonymous

現状

キャンバスに任意の画像をアップロードし、キャンバスに描画する機能を実装しています。
下記リンク先で動作をご確認いただけます。

demo

キャンバスに画像を読み込むと、キャンバスとフッターがフェードインします。
フッターはスクロールに追尾します。

使用しているライブラリ

・jQuery
・Fabric.js

問題点

一番下までスクロールしても、フッターがキャンバスに被ってしまいます。
縦長の画像を読み込んでいただくと、問題となっている状態が分かりやすいです。

縦長の画像を用意したので、よろしければご使用ください。
画像出典:pixabay

サンプル画像

解決したいこと、ご助力いただきたいこと

一番下までスクロールした際、フッターがキャンバスに被らないようにしたいです。
また、デバイスやディスプレイサイズを問わない方法で解決したいです。

試したこと

CSSで#cnvAreapadding-bottomを設定すると、一番下までスクロールした際の、フッターとキャンバスの被りを回避できました。

しかしpadding-bottomで設定した値は、ディスプレイのサイズによって必要な値が異なります。
例えば上記のdemoで言うと、Outputタブのみを表示した場合の大きいウインドウサイズでは被りを回避できましたが、HTML、CSS、JavaScriptタブを表示した場合の小さいウインドウサイズでは、被りを回避できませんでした。

ソースコード

_x000D_

_x000D_

$('#file').on('change', function () {_x000D_
    $('#view').fadeIn();_x000D_
    var fr = new FileReader();_x000D_
    fr.onload = function (e) {_x000D_
        input(e.target.result);_x000D_
    };_x000D_
    fr.readAsDataURL(this.files[0]);_x000D_
});_x000D_
_x000D_
var canvas = new fabric.Canvas('c');_x000D_
_x000D_
var input = function (url) {_x000D_
    fabric.Image.fromURL(url, function (img){_x000D_
      var wS = canvas.width / img.width;_x000D_
      img.scale(wS);_x000D_
      canvas.setHeight(img.height * wS);_x000D_
      canvas.clear();_x000D_
      canvas.add(img).renderAll();_x000D_
      img.selectable = false;_x000D_
    });_x000D_
};

_x000D_

html{_x000D_
  text-align: center;_x000D_
}_x000D_
_x000D_
#view{_x000D_
  display: none;_x000D_
}_x000D_
_x000D_
.canvas-container{_x000D_
 margin: auto_x000D_
}_x000D_
_x000D_
/* #cnvArea{_x000D_
  padding-bottom: 25%;_x000D_
} */_x000D_
_x000D_
canvas{_x000D_
  border: 2px solid black;_x000D_
}_x000D_
_x000D_
#optArea{_x000D_
  min-height: 40%;_x000D_
  position: fixed;_x000D_
  width: 100vw;_x000D_
  left: 0;_x000D_
  bottom: 0;_x000D_
  background-color: black;_x000D_
}

_x000D_

<!DOCTYPE html>_x000D_
<html>_x000D_
  <head>_x000D_
    <meta charset="utf-8">_x000D_
    <meta name="viewport" content="width=device-width">_x000D_
    <title>test</title>_x000D_
  </head>_x000D_
  <body>_x000D_
    <input id="file" type="file" accept="image/*">_x000D_
    <div id="view">_x000D_
      <div id="cnvArea">_x000D_
        <canvas id="c" width="700"></canvas>_x000D_
      </div>_x000D_
      <div id="optArea">_x000D_
        <input type="range" id="value" value="0.5" min="0" max="1" step="0.01">_x000D_
        <br>_x000D_
        <button>a</button>_x000D_
        <button>b</button>_x000D_
        <button>c</button>_x000D_
        <button>d</button>_x000D_
      </div>_x000D_
    </div>_x000D_
    <script src="https://code.jquery.com/jquery-3.1.0.js"></script>_x000D_
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.3.2/fabric.min.js"></script>_x000D_
    </body>_x000D_
</html>

_x000D_

_x000D_

_x000D_

解決

position プロパティに fixed を指定した場合、そのボックスはビューポートに対して固定されます[1]

9.3.1 Choosing a positioning scheme: ‘position’ property[1]

The box’s position is calculated according to the ‘absolute’ model, but in addition, the box is fixed with respect to some reference. As with the ‘absolute’ model, the box’s margins do not collapse with any other margins. In the case of handheld, projection, screen, tty, and tv media types, the box is fixed with respect to the viewport and does not move when scrolled.

よって、フッターへ対して設定している min-height プロパティの値は、ビューポートを基準とした相対的な高さを取ることになります。また、 CSS Values and Units Module Level 3 にて追加された vh という単位は、初期包含ブロックの高さの割合を取ります[2]。これらより、当該 min-height プロパティの単位はパーセントから vh へ置き換えることが出来ることがわかります。

§ 5.1.2. Viewport-percentage Lengths: the vw, vh, vmin, vmax units[2]

vh unit
Equal to 1% of the height of the initial containing block.

このようにすることで、 padding-bottom プロパティを用いてフッターと同じ高さの余白を作ることが出来るようになり、ページ最下部にてフッターとキャンバスが重なる問題を解決することが出来ます。

body {
  padding-bottom: 40vh;  /* 追加 */
}

#optArea {
  min-height: 40vh;  /* 変更 */
  position: fixed;
  width: 100vw;
  left: 0;
  bottom: 0;
  background-color: black;
}

_x000D_

_x000D_

$('#file').on('change', function() {_x000D_
  $('#view').fadeIn();_x000D_
  var fr = new FileReader();_x000D_
  fr.onload = function(e) {_x000D_
    input(e.target.result);_x000D_
  };_x000D_
  fr.readAsDataURL(this.files[0]);_x000D_
});_x000D_
_x000D_
var canvas = new fabric.Canvas('c');_x000D_
_x000D_
var input = function(url) {_x000D_
  fabric.Image.fromURL(url, function(img) {_x000D_
    var wS = canvas.width / img.width;_x000D_
    img.scale(wS);_x000D_
    canvas.setHeight(img.height * wS);_x000D_
    canvas.clear();_x000D_
    canvas.add(img).renderAll();_x000D_
    img.selectable = false;_x000D_
  });_x000D_
};

_x000D_

html {_x000D_
  text-align: center;_x000D_
}_x000D_
_x000D_
body {_x000D_
  padding-bottom: 40vh;  /* 追加 */_x000D_
}_x000D_
_x000D_
#view {_x000D_
  display: none;_x000D_
}_x000D_
_x000D_
.canvas-container {_x000D_
  margin: auto_x000D_
}_x000D_
_x000D_
canvas {_x000D_
  border: 2px solid black;_x000D_
}_x000D_
_x000D_
#optArea {_x000D_
  min-height: 40vh;  /* 変更 */_x000D_
  position: fixed;_x000D_
  width: 100vw;_x000D_
  left: 0;_x000D_
  bottom: 0;_x000D_
  background-color: black;_x000D_
}

_x000D_

<!DOCTYPE html>_x000D_
<html>_x000D_
_x000D_
<head>_x000D_
  <meta charset="utf-8">_x000D_
  <meta name="viewport" content="width=device-width">_x000D_
  <title>test</title>_x000D_
</head>_x000D_
_x000D_
<body>_x000D_
  <input id="file" type="file" accept="image/*">_x000D_
  <div id="view">_x000D_
    <div id="cnvArea">_x000D_
      <canvas id="c" width="700"></canvas>_x000D_
    </div>_x000D_
    <div id="optArea">_x000D_
      <input type="range" id="value" value="0.5" min="0" max="1" step="0.01">_x000D_
      <br>_x000D_
      <button>a</button>_x000D_
      <button>b</button>_x000D_
      <button>c</button>_x000D_
      <button>d</button>_x000D_
    </div>_x000D_
  </div>_x000D_
  <script src="https://code.jquery.com/jquery-3.1.0.js"></script>_x000D_
  <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.3.2/fabric.min.js"></script>_x000D_
</body>_x000D_
_x000D_
</html>

_x000D_

_x000D_

_x000D_


今回の場合は当てはまりませんが、もし高さがビューポートによらない場合、 CSS だけで解決をすることが難しいため、 JavaScript により動的にフッターの高さを取得し、それを余白の高さとして適用する方法が簡単です。

$(window).on("resize", function() {
  var optAreaHeight = $("#optArea").outerHeight();
  $("#view").css("padding-bottom", optAreaHeight);
});

_x000D_

_x000D_

$('#file').on('change', function() {_x000D_
  $('#view').fadeIn();_x000D_
  var fr = new FileReader();_x000D_
  fr.onload = function(e) {_x000D_
    input(e.target.result);_x000D_
  };_x000D_
  fr.readAsDataURL(this.files[0]);_x000D_
});_x000D_
_x000D_
$(window).on("resize", function() { // 追加_x000D_
  var optAreaHeight = $("#optArea").outerHeight();_x000D_
  $("#view").css("padding-bottom", optAreaHeight);_x000D_
});_x000D_
_x000D_
var canvas = new fabric.Canvas('c');_x000D_
_x000D_
var input = function(url) {_x000D_
  fabric.Image.fromURL(url, function(img) {_x000D_
    var wS = canvas.width / img.width;_x000D_
    img.scale(wS);_x000D_
    canvas.setHeight(img.height * wS);_x000D_
    canvas.clear();_x000D_
    canvas.add(img).renderAll();_x000D_
    img.selectable = false;_x000D_
  });_x000D_
};

_x000D_

html {_x000D_
  text-align: center;_x000D_
}_x000D_
_x000D_
#view {_x000D_
  display: none;_x000D_
}_x000D_
_x000D_
.canvas-container {_x000D_
  margin: auto_x000D_
}_x000D_
_x000D_
canvas {_x000D_
  border: 2px solid black;_x000D_
}_x000D_
_x000D_
#optArea {_x000D_
  min-height: 40%;_x000D_
  position: fixed;_x000D_
  width: 100vw;_x000D_
  left: 0;_x000D_
  bottom: 0;_x000D_
  background-color: black;_x000D_
}

_x000D_

<!DOCTYPE html>_x000D_
<html>_x000D_
_x000D_
<head>_x000D_
  <meta charset="utf-8">_x000D_
  <meta name="viewport" content="width=device-width">_x000D_
  <title>test</title>_x000D_
</head>_x000D_
_x000D_
<body>_x000D_
  <input id="file" type="file" accept="image/*">_x000D_
  <div id="view">_x000D_
    <div id="cnvArea">_x000D_
      <canvas id="c" width="700"></canvas>_x000D_
    </div>_x000D_
    <div id="optArea">_x000D_
      <input type="range" id="value" value="0.5" min="0" max="1" step="0.01">_x000D_
      <br>_x000D_
      <button>a</button>_x000D_
      <button>b</button>_x000D_
      <button>c</button>_x000D_
      <button>d</button>_x000D_
    </div>_x000D_
  </div>_x000D_
  <script src="https://code.jquery.com/jquery-3.1.0.js"></script>_x000D_
  <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.3.2/fabric.min.js"></script>_x000D_
</body>_x000D_
_x000D_
</html>

_x000D_

_x000D_

_x000D_

回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *