20200602のHTMLに関する記事は5件です。

お絵かきできるSNSを作りたい!9

お絵かきできるSNSを作りたい後半戦、下のメニューを作っていきます。

まずはPNGで保存機能。

function saveCanvas(){
    var src1_img = new Image();
    var src2_img = new Image();
    src1_img.src = $("#canvas")[0].toDataURL();
    src1_img.addEventListener('load', function() {
        src2_img.src = $("#canvas2")[0].toDataURL();
        src2_img.addEventListener('load', function() {
            var dst_canvas = document.createElement('canvas');
            dst_canvas.width = 800;
            dst_canvas.height = 600;
            dst_canvas.getContext("2d").drawImage(src1_img, 0, 0, 800, 600);
            dst_canvas.getContext("2d").drawImage(src2_img, 0, 0, 800, 600);

            var imageType = "image/png";
            var fileName = "download.png";
            // base64エンコードされたデータを取得 「data:image/png;base64,iVBORw0k~」
            var base64 = dst_canvas.toDataURL(imageType);
            // base64データをblobに変換
            var blob = Base64toBlob(base64);
            // blobデータをa要素を使ってダウンロード
            saveBlob(blob, fileName);
        }, false);
    }, false);
}

// Base64データをBlobデータに変換
function Base64toBlob(base64){
    // カンマで分割して以下のようにデータを分ける
    // tmp[0] : データ形式(data:image/png;base64)
    // tmp[1] : base64データ(iVBORw0k~)
    var tmp = base64.split(',');
    // base64データの文字列をデコード
    var data = atob(tmp[1]);
    // tmp[0]の文字列(data:image/png;base64)からコンテンツタイプ(image/png)部分を取得
    var mime = tmp[0].split(':')[1].split(';')[0];
    //  1文字ごとにUTF-16コードを表す 0から65535 の整数を取得
    var buf = new Uint8Array(data.length);
    for (var i = 0; i < data.length; i++) {
        buf[i] = data.charCodeAt(i);
    }
    // blobデータを作成
    var blob = new Blob([buf], { type: mime });
    return blob;
}

// 画像のダウンロード
function saveBlob(blob, fileName){
    var url = (window.URL || window.webkitURL);
    // ダウンロード用のURL作成
    var dataUrl = url.createObjectURL(blob);
    // イベント作成
    var event = document.createEvent("MouseEvents");
    event.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
    // a要素を作成
    var a = document.createElementNS("http://www.w3.org/1999/xhtml", "a");
    // ダウンロード用のURLセット
    a.href = dataUrl;
    // ファイル名セット
    a.download = fileName;
    // イベントの発火
    a.dispatchEvent(event);
}

無駄に関数が増えました。
レイヤーが2枚あるので1枚にがっちゃんこしてます。
[github]修正内容はこちら

次はフルスクリーンボタンの実装です。

var blFullscreen = true;
function doFullscreen(){
    var elem = document.getElementById("screen");
    if (blFullscreen){
        if (elem.requestFullscreen) {
          elem.requestFullscreen();
        } else if (elem.webkitRequestFullScreen) {
          elem.webkitRequestFullScreen();
        } else if (elem.mozRequestFullScreen) {
          elem.mozRequestFullScreen();
        } else if (elem.msRequestFullscreen) {
          elem.msRequestFullscreen();
        }
        blFullscreen = false;
    } else {
        if (document.webkitCancelFullScreen) {
            document.webkitCancelFullScreen();
        } else if (document.mozCancelFullScreen) {
            document.mozCancelFullScreen();
        } else if (document.msExitFullscreen) {
            document.msExitFullscreen();
        } else if(document.cancelFullScreen) {
            document.cancelFullScreen();
        } else if(document.exitFullscreen) {
            document.exitFullscreen();
        }
        blFullscreen = true;
    }
}

フルスクリーン状態の判定をJSで行おうとするとブラウザによって挙動が怪しくなり悩むくらいならとグローバルおじさんになりました。

あと細かい修正もしたので詳しくは↓こちらを。
[github]修正内容はこちら

そして、左右反転。
これがちょっと手間で、左右反転するとレイヤー1とレイヤー2のX座標も反転させています。
メインの関数は↓こちら。

var blReflect = true;
function doReflect(){
    if (blReflect){
        $("#canvas").css('transform', 'scale(-1, 1)');
        $("#canvas2").css('transform', 'scale(-1, 1)');
        blReflect = false;
    } else {
        $("#canvas").css('transform', 'scale(1, 1)');
        $("#canvas2").css('transform', 'scale(1, 1)');
        blReflect = true;
    }
}

あとは、押したとき、動かしている時、離したときに左右反転状態かを判定してX座標を左右逆になるようにしました。

        if (blReflect) {
            ox=event.clientX-event.target.getBoundingClientRect().left;
        } else {
            ox=$("#canvas").width() + (parseInt($("body").css('padding-left')) * 2) - event.clientX-event.target.getBoundingClientRect().left;
        }

[github]修正内容はこちら

今回最後は最初から書き直すの実装です。
これはページリロードでいいんじゃ・・?とも思いましたが、折角なのでちゃんと実装しました。

function doClear(){
    if(confirm('本当にいいんですね?')){
        ct.clearRect(0, 0, $("#canvas").width(), $("#canvas").height());
        ct2.clearRect(0, 0, $("#canvas2").width(), $("#canvas2").height());
    }
}

ちゃんとアラートで消すことを確認する新設設計。
レイヤー1とレイヤー2をクリアしています。

[github]修正内容はこちら

次回、Ctrl+Zをしたい。お楽しみに。

最初:お絵かきできるSNSを作りたい!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

lightbox2の使い方とカスタマイズについて(メモ)

この投稿について

作業進捗の備忘録、学習効果の向上を目的として記録していきます。
学習しながら作業を進めておりますので、情報が間違っている場合も多々あると思います。
正しい情報は公式ドキュメントを参考にしてください。

開発環境

  • macOS High Sierra 10.13.6
  • Anaconda 3
  • Python 3.7.7
  • Djnago 3.0.6
  • VisualStudioCode

※記事内のHTMLコードやフォルダ構成についてはアプリ開発途中のものを使用していますので、あらかじめご了承ください。

lightbox2とは

公式サイト
https://lokeshdhakar.com/projects/lightbox2/

Webサイト上でいい感じに写真を閲覧できるようにするためのJavaScriptライブラリです。

lightbox2でやりたいこと

ページをめくる感じに写真を次々閲覧したい。
なんかいい感じに写真を見せたい。

導入してみる

lightbox.jsを公式のGithub(https://github.com/lokesh/lightbox2/releases)からダウンロードし、それぞれアプリ内のstaticフォルダ内に設置します。
今回は少しカスタマイズして使用したいので、jsはminファイルではなく、オリジナルのものを使いました。

app/static/js/lightbox.js

lightbox.cssはダウンロードせずに、

内で読み込んで使用することにしました。

HTMLの記述

head内にそれぞれ必要なファイルを読み込むための記述をします。jQueryも使うみたいなのでついでに読み込みます。

base.html
<head>
......
<link href="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.7.1/css/lightbox.css" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="{% static 'js/lightbox.js' %}"></script>
</head>

次に、写真を表示するページです。

index.html
{% extends 'base.html' %}

{% block bodyblock %}
<div class="row pb-5">
    {% for pic in obj %}
    <div class="col-md-3 mt-5">
        <a href="{{ pic.picture.url }}" data-lightbox="group"> 
            <img src="{{pic.pic_thumbnail.url}}" class='rounded' width="250" height="250">
        </a>
    </div>
    {% endfor %}
</div>
{% endblock %}

写真DBからクエリセットを取得してobjに読み込んでいます。
for文でサムネイルを並べるよう記述しています。

imgタグを内包しているaタグにdata-lightbox="group"の属性をつけることで、ページめくりの対象となる画像であることが認識されます。

動作確認

写真のサムネイルを並べているページにアクセスし、写真をクリックすると、ポップアップウインドウが表示され、写真の左右にある矢印をクリックすると、隣の画像が表示されます。
(公式サイトサンプルのような動作が確認できると思います。)

カスタマイズしてみる

画像を表示させるだけだと少し物足りなく感じたので、写真情報編集用のボタン(公開非公開、編集、削除など)を表示させることを試みました。
情報を探していると、写真が表示されるウインドウにキャプションを入れられるようで、方法は、先ほどdata-lightbox="group"の属性を追加したタグに、data-title="キャプションしたい文字列"の属性と属性値を追加すれば良いとのこと。

以下のようにhtmlを編集すれば、タイトルのキャプションをつけるのは簡単そうです。※モデルにtitleフィールドがある場合。

index.html
{% extends 'base.html' %}

{% block bodyblock %}
<div class="row pb-5">
    {% for pic in obj %}
    <div class="col-md-3 mt-5">
        <a href="{{ pic.picture.url }}" data-lightbox="group" data-title="{{ pic.title }}"> 
            <img src="{{pic.pic_thumbnail.url}}" class='rounded' width="250" height="250">
        </a>
    </div>
    {% endfor %}
</div>
{% endblock %}

今回は編集用のボタンをキャプション部分に表示してみます。

lightbox.jsを編集してみる

lightbox.js(114行目)
    // Github issue: https://github.com/lokesh/lightbox2/issues/663
    $('<div id="lightboxOverlay" tabindex="-1" class="lightboxOverlay"></div><div id="lightbox" tabindex="-1" class="lightbox"><div class="lb-outerContainer"><div class="lb-container"><img class="lb-image" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" alt=""/><div class="lb-nav"><a class="lb-prev" aria-label="Previous image" href="" ></a><a class="lb-next" aria-label="Next image" href="" ></a></div><div class="lb-loader"><a class="lb-cancel"></a></div></div></div><div class="lb-dataContainer"><div class="lb-data"><div class="lb-details"><span class="lb-caption"></span><span class="lb-number"></span></div><div class="lb-closeContainer"><a class="lb-close"></a></div></div></div></div>').appendTo($('body'));

ここで、画像を表示するポップアップウインドウのHTMLを発行しているようです。このあたりを編集することでレイアウト等をカスタマイズできそうです。

編集後
・・・・・<span class="lb-caption"></span><div class="lightbox-buttons"><a class="update" href="#"></a></div><span class="lb-number"></span>・・・・・

既存のタグは改変したくなかったので、captionを表示するためのタグの後ろに、ボタンを格納するための<div class="lightbox-buttons"><a class="update btn btn-primary" href="#">Update</a></div>を追記しました。

次に、index.htmlを以下のように編集します。

index.html
{% extends 'base.html' %}

{% block bodyblock %}
<div class="row pb-5">
    {% for pic in obj %}
    <div class="col-md-3 mt-5">
        <a href="{{ pic.picture.url }}" data-lightbox="group" update_url="{% url 'update' %}"> 
            <img src="{{pic.pic_thumbnail.url}}" class='rounded' width="250" height="250">
        </a>
    </div>
    {% endfor %}
</div>
{% endblock %}

lightbox.js(220~226行目)
    function addToAlbum($link) {
      self.album.push({
        alt: $link.attr('data-alt'),
        link: $link.attr('href'),
        title: $link.attr('data-title') || $link.attr('title')
      });
    }

この辺りのコードで、index.htmlのaタグ内に記載したdata-title属性の属性値を取得しているようです。

なので、以下のように編集します。

編集後
    function addToAlbum($link) {
      self.album.push({
        alt: $link.attr('data-alt'),
        link: $link.attr('href'),
        update: $link.attr('update_url'),
      });
    }

これにより、index.htmlのupdare_urlの属性値({% url 'update' %})が読み取られ、albumのupdateに格納されます。

lightbox.js(468~483行目)
// Display caption, image number, and closing button.
  Lightbox.prototype.updateDetails = function() {
    var self = this;

    // Enable anchor clicks in the injected caption html.
    // Thanks Nate Wright for the fix. @https://github.com/NateWr
    if (typeof this.album[this.currentImageIndex].title !== 'undefined' &&
      this.album[this.currentImageIndex].title !== '') {
      var $caption = this.$lightbox.find('.lb-caption');
      if (this.options.sanitizeTitle) {
        $caption.text(this.album[this.currentImageIndex].title);
      } else {
        $caption.html(this.album[this.currentImageIndex].title);
      }
      $caption.fadeIn('fast');
    }

このコードで、220~226行で作成したalbumのtitleと紐づいている値の存在を確認して、データがあるようであれば114行目で生成した<span class="lb-caption"></span>にHTMLなりテキストなりを入力する処理をしています。

なので、以下のように編集します。

468~483行目
// Display caption, image number, and closing button.
  Lightbox.prototype.updateDetails = function() {
    var self = this;

    // Enable anchor clicks in the injected caption html.
    // Thanks Nate Wright for the fix. @https://github.com/NateWr
    //update url injection.
    if (typeof this.album[this.currentImageIndex].update !== 'undefined' &&
      this.album[this.currentImageIndex].update !== '') {
      var $caption = this.$lightbox.find('.update');
      $caption.attr('href',this.album[this.currentImageIndex].update);
      $caption.fadeIn('fast');
    }

これにより、albumのupdateと紐づいている値の存在を確認して、データがあるようであれば114行目で生成した`'のhref値を指定したurlに書き換えることができます。
これでlightboxのキャプション部分にリンクを設定することができました。

その他

285行目
this.$lightbox.find('.lb-image, .lb-nav, .lb-prev, .lb-next, .lb-dataContainer, .lb-numbers, .lb-caption').hide();

上記に独自に追加した属性を追記しないと、意図しないところでボタンが表示されたままになってしまったりするので、記載しておいた方が良い。

編集後
this.$lightbox.find('.lb-image, .lb-nav, .lb-prev, .lb-next, .lb-dataContainer, .lb-numbers, .lb-caption, .update').hide();



  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

2020/06/02 プログラミング(WEBデザイン)学習3日目 メモ

本日の学習範囲

ドットインストール

・はじめてのCSS #05〜#07
(学習時間:1時間)

CSS学習メモ

background-colorプロパティ

背景色を指定する

ボックス

要素によって生成される領域
内から順に、content・padding・border・marginで成り立っている

content

widthプロパティとheightプロパティで指定する領域、要素内容が表示される

padding

paddingプロパティで指定する内側余白、背景が適用される

border

borderプロパティで指定する境界線、太さ・線種・色を指定できる

margin

marginプロパティで指定する外側余白

marginプロパティ

{margin-top: 幅;
margin-right: 幅;
margin-bottom: 幅;
margin-left: 幅;}

ボックスのマージンの幅を指定する
{margin: 上の幅 右の幅 下の幅 左の幅;}と記述することも可能
全て同じ値の場合は、{margin: 幅;}と記述できる

divタグ(HTML)

<div></div>
ひとかたまりの範囲として定義する

class属性(HTML)

<div class="class名">
要素に分類名を指定する

classセレクタ

.クラス名 {}
特定のclass名がつけられた要素にスタイルを適用する

auto

ユーザーエージェントによって自動的に算出される値
autoが適用されたプロパティ毎に効果は異なる
marginプロパティにおけるautoは、ブラウザが適切なマージンを選択する
{margin-right: auto;
margin-left: auto;

の場合、左右中央揃えになる

ユーザーエージェント

サイトにアクセスした際に送る、OSやブラウザ、機種などの情報

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

HTMLでローカルにある画像を表示する

プログラミングの勉強日記

2020年6月2日 Progate Lv.83
ウェブページ制作中

imgを用いてローカルファイルにある画像を表示する方法

 HTMLファイルが保存されている同じ階層に表示させたい画像を入れたフォルダを作成して、そのなかの画像名を指定する。 

<img src="./photo/S__520912916.jpg">

0602.PNG

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者によるプログラミング学習ログ 332日目

100日チャレンジの332日目

twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。
100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。
332日目は、

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む