20200211のHTMLに関する記事は13件です。

フロントエンドエンジニアとして6か月働いてコードレビューで指摘されたこと3つ

1.見た目とデザインを合わせる

自分が作ったものとデザイナーからもらったXDファイルの見た目が異なっていた。

(※Adobe XD は、ユーザー操作性をデザイン、プロトタイプ化、共有するためのプラットフォームです。ワイヤフレーム、ビジュアルデザイン、インタラクションデザイン、プロトタイプ化、プレビュー、共有間の切り替えが簡単な、オールインワンの強力なツールです。(公式サイトより))以下、XD

どういう指摘をされたか

H殿、K氏「ここが違うよ」
例えば、XDでは文字色が白なのに、書いたコードでは黒になっていると単純なものであった。
また、自分はPC版からコードを書くため、SP版用文字サイズの変更し忘れのミスを一番指摘された。

それはなぜか

自分が書いたコードが、間違っていた。
単純に見落としでの間違いだった。
コードレビューの前に自分で最終確認をしていなかったためである。

どのように直したか

一か所ずつ確認するようになった。

もちろん、XDを確認しながらコードを書くが、一通り書き終えてから
XDの文字サイズ、文字色、余白の幅などを再確認するようになった。

まだまだ指摘はされますが....

2.不要なコードの削除

コードレビューの前にまたまた最終確認をしておらず指摘をされたこと....

どういう指摘をされたか

H殿、K氏「これいらないよね」
CSSでXDファイルでファイル通りの見た目にするために実験的に書いていたCSSプロパティが不要だった。

それはなぜか

1.見た目とデザインを合わせるでも記載したが、コードレビューの前に自分が書いたコードの最終確認を怠ったためである。

どのように直したか

実験的に書いたコードは、確認し終えたらすぐ消すように習慣づけるようにした。
また、最終確認の際も気を付けてコードを確認するようになった。

当たり前のことなのにできていなかったためミスを起こしていた.......

3.クラス名

どういうコードを書いたか
<!-- 修正前 -->
<div class="header-contents-logo">

<!-- 修正後 -->
<div class="header-logo">

修正前は、見たままの入れ子構造通りに書いていた。

どういう指摘をされたか

H殿、K氏「クラス名、関数名のつけ方が下手糞」

H殿「logoの親コンポーネントはなにー?」

ぼく「header.....」

H殿「header-logoでいい」

それはなぜか

まだ、クラス名のつけ方に慣れていなかった。
親コンポーネントなどの規則を知らなかったため。

どのように直したか

書き方を知らなかったので、
他サイトのクラス名をディベロッパーツールで確認、Bootstrapで学習するようにした。
また、CSSの命名規則を調べるようになった。

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

[Web] 要素を分割せずに済むグリッチエフェクト ~SVGフィルターのキホン~

Note: この記事に掲載しているCodePenはSVGフィルターを使用しており、ChromeとFirefox以外のブラウザーでは正常に表示されない可能性がある。

概要

この記事は?の記事にインスパイアされたものである。

CSSでグリッチっぽい表現をやる

グリッチエフェクトを表現するには文字や画像を横にスライスして横方向に少し位置をずらす必要がある。上の記事はスライスごとにHTMLの要素を用意するという方法をとっており、同じテキスト(or 画像)を持った要素をスライスの数だけ用意する必要がある。ここではSVGフィルターを用いた、要素1個で済むグリッチエフェクトを紹介する(厳密には、<svg>とフィルター要素を除いて1個)。このエフェクトはテキストや画像、SVGで表現できるあらゆる図形に適用できる。

最終的には?のようなものができる(ループ可)。横に分割してずらすだけでなく、色収差エフェクトを加えることでよりぽくなっている。

glitch

CodePenのデモ?(普通にテキストをマウスで選択するができる)

CodePenで開く

この記事は、SVGフィルターの簡単な説明から始めて、少しずつコードを構築していく形を取る。

SVGフィルター

SVGはベクター形式の画像であり、HTMLの<img>タグで外部SVGファイルを表示させることができるだけでなく、HTML内に直接SVGを書くことができる。また、SVGの中にはテキストや四角形、円などいろいろな図形を含めることができるが、それらに対してフィルター効果を付与することができる。

フィルターは普通<svg>内の<defs>内で宣言する。フィルターは一度宣言すれば何度も参照でき、同じページ内の別の<svg>からでも参照することができる

svgfilter-def.png

<filter>の中では"ぼかしフィルター"や"色変換フィルター"などの基本的なフィルター処理(フィルタープリミティブ)を組み合わせることで、全体として一つのフィルターを定義する。今回用いるフィルタープリミティブは<feOffset><feColorMatrix><feBlend><feMerge>の4つである。これ以外にもいくつかあるが、ここではこの4つだけ説明する。

<feOffset>: オフセット

これは非常に単純で、入力画像をx軸、y軸方向に指定した長さだけ平行移動(オフセット)させるものである。

See the Pen SVG feOffset Filter by righteous (@righteous_github) on CodePen.

後で説明するが、ここでは<filter>primitiveUnits="objectBoundingBox"が指定されているため、dx dyに指定した値はフィルターが適用されている要素の幅、高さに対する割合となる。今の場合、dxが0.1なので<image>の幅200に0.1を掛けた20だけx軸方向に移動することになる。

<feColorMatrix>: 行列による色変換

これは入力画像の各ピクセルのRGBA値を行列によって変換するものである。数式で表すと?のようになる。$R,G,B,A$が入力の各ピクセルのRGBA値であり、$R',G',B',A'$が出力の値である。単にRGBAの列ベクトルに指定した行列を掛けるだけなので、行列の乗算を知らない人はぜひ調べてみてほしい。実際指定できるのは$a_1$から$a_{20}$の部分だけであり、これらをvaluesに並べて指定する。

\begin{bmatrix}
R'\\
G'\\
B'\\
A'\\
1
\end{bmatrix} = 
\begin{bmatrix}
a_1 & a_2 & a_3 & a_4 & a_5\\
a_6 & a_7 & a_8 & a_9 & a_{10}\\
a_{11} & a_{12} & a_{13} & a_{14} & a_{15}\\
a_{16} & a_{17} & a_{18} & a_{19} & a_{20}\\
0 & 0 & 0 & 0 & 1\\
\end{bmatrix}
\begin{bmatrix}
R\\
G\\
B\\
A\\
1
\end{bmatrix}

例えば次のような行列を指定すれば、入力画像の緑成分だけ取り出すことができる。

\begin{bmatrix}
R'\\
G'\\
B'\\
A'\\
1
\end{bmatrix} = 
\begin{bmatrix}
0 & 0& 0& 0& 0\\
0 & 1& 0& 0 & 0\\
0 & 0& 0& 0 & 0\\
0 & 0& 0& 1 & 0\\
0 & 0 & 0 & 0 & 1\\
\end{bmatrix}
\begin{bmatrix}
R\\
G\\
B\\
A\\
1
\end{bmatrix} = 
\begin{bmatrix}
0\\
G\\
0\\
A\\
1
\end{bmatrix}

See the Pen SVG feColorMatrix Filter by righteous (@righteous_github) on CodePen.

<feBlend>: ブレンド

これは画像編集ソフトにおけるレイヤーのブレンドと同じようなもので、2つの入力画像を指定したブレンドモードで合成する。?はdarkenモードでブレンドしたものである。

See the Pen SVG feBlend Filter by righteous (@righteous_github) on CodePen.

<feComposite>: 合成

これは<feBlend>と同じようなものであるが、ブレンドモードは固定でnormalで合成する(画像編集ソフトにおけるレイヤーのデフォルトのブレンドモードと同じである)。ただし<feBlend>と違い、3個以上の入力画像を指定できるため、コードがコンパクトになる。下は画像の上に緑色の四角を重ね、さらに透明度0.5の青色の四角を重ねている。

See the Pen SVG feMerge Filter by righteous (@righteous_github) on CodePen.

フィルタープリミティブの適用範囲の指定

<filter>とすべてのフィルタープリミティブは、フィルターを適用する範囲をx y width height で指定することができる。例えば<feOffset>を適用すると元の要素の範囲をはみ出すことになるため、それも表示されてほしいならば<filter>の範囲を要素の範囲より広めに取る必要がある。親切にも<filter>の範囲のデフォルト値はx="-10%" y="-10%" width="120%" height="120%"となっており、少しはみ出しても表示されるようになっている。<filter>の範囲をはみ出すような範囲をフィルタープリミティブに指定しても意味はない。

補足: <filter>primitiveUnits属性について

フィルタープリミティブで指定する色々な座標や長さは<filter>primitiveUnits属性の影響を受ける。

  • primitiveUnits="userSpaceOnUse": このフィルターが適用された要素の座標系における値(パーセント値を指定した場合、その要素を含むSVG要素のサイズに対する割合)
  • primitiveUnits="objectBoundingBox": このフィルターが適用された要素のサイズに対する割合(0.1なら0.1倍)

例えばフィルタープリミティブの1つ<feOffset>dx0.5のとき、userSpaceOnUseの場合は、フィルターが適用される側の要素上での0.5ピクセルとなり、objectBoundingBoxの場合はフィルターが適用される側の要素のサイズの0.5倍となる。

ここではChromeのバグ1を避けるためprimitiveUnits="objectBoundingBox"を使用している。

グリッチエフェクトの実装

グリッチエフェクトは適用される要素のサイズに対して、横に少しはみ出す形になるため、左右に少し余裕を持ってフィルターの領域を指定する。広げすぎるとパフォーマンスに影響が出るため、必要最小限にするのがよい。

<filter id="glitch" primitiveUnits="objectBoundingBox" x="-10%" y="0%" width="120%" height="100%">
</filter>

svg filter.png

色収差エフェクト

色収差とは光の波長によって屈折率が異なるために、レンズを通して撮影すると輪郭が滲んだような写真になる現象である。ここでは色収差を表現するために、簡易的に入力画像をRとGとBに分解し、等間隔にずらす、という方法をとる。イメージとしては?のような感じである。

CodePenで開く: Chromatic Aberration Effect Visualization

(このCodePenはどのように色収差を表現するかを視覚化するためのものであって、これ自体はSVGフィルターを使っていない)

RGB各成分の抽出

まずは入力画像からR成分G成分B成分をそれぞれ抽出する必要がある。これは先述のとおり<feColorMatrix>を使って?のように実現することができる。フィルタープリミティブは入力画像をin属性で指定することができ、result属性で出力に名前をつけることができる。いずれもin属性にSourceGraphicを指定しているが、これは文字通り元の画像(フィルターを適用しようとしている図形の、フィルターを適用する前のレンダリング結果)である。各成分は後で使用するため、resultでそれぞれの出力に名前(red green blue)をつけている。

<filter id="glitch" primitiveUnits="objectBoundingBox" x="-10%" y="0%" width="120%" height="100%">
  <feColorMatrix in="SourceGraphic" result="red" type="matrix" values="1 0 0 0 0
                                                                        0 0 0 0 0
                                                                        0 0 0 0 0
                                                                        0 0 0 1 0" />
  <!-- green: G成分 -->
  <feColorMatrix in="SourceGraphic" result="green" type="matrix" values="0 0 0 0 0
                                                                          0 1 0 0 0
                                                                          0 0 0 0 0
                                                                          0 0 0 1 0" />
  <!-- blue: B成分 -->
  <feColorMatrix in="SourceGraphic" result="blue" type="matrix" values="0 0 0 0 0
                                                                        0 0 0 0 0
                                                                        0 0 1 0 0
                                                                        0 0 0 1 0" />
</filter>

各成分を横にずらす

次に、R成分を左に、B成分を右にずらす。これは明らかに<feOffset>の出番である。次のように、上で抽出したR成分とB成分をinで参照し、±0.005(×幅)だけ左右にずらす。

<filter id="glitch" primitiveUnits="objectBoundingBox" x="-10%" y="0%" width="120%" height="100%">
  <!-- ...省略... -->

  <!-- red-shifted: R成分を左にずらしたもの -->
  <feOffset in="red" result="red-shifted" dx="-0.005" dy="0" />
  <!-- blue-shifted: B成分を右にずらしたもの -->
  <feOffset in="blue" result="blue-shifted" dx="0.005" dy="0" />
</filter>

ブレンド

最後に3つの成分を重ね合わせて一つの画像にする。これはお察しの通り<feBlend>を使う。色が明るくなる方向にブレンドしたいので、screenモードを使用する。<feBlend>は2つの画像しか入力できないため、3つの画像を合成するには2回<feBlend>を適用する必要がある。

<filter id="glitch" primitiveUnits="objectBoundingBox" x="-10%" y="0%" width="120%" height="100%">
  <!-- ...省略... -->

  <!-- blended: ブレンド結果 -->
  <feBlend mode="screen" in="red-shifted" in2="green" result="red-green" />
  <feBlend mode="screen" in="red-green" in2="blue-shifted" result="blended" />
</filter>

これで、色収差エフェクトの完成である。

CodePenで開く

横ずれエフェクト

スライスしてずらす

次に、画像を横向きにいくつかスライスして、さらに一部のスライスを横向きに色々な距離動かす必要がある。そのためには、<feOffset>各スライスの範囲にだけ適用する必要がある。

先述の通り、各フィルタープリミティブはx y width height でその適用範囲を指定することができる。このとき、その出力における指定した範囲外の領域はすべて透明となる。スライスごとに<feOffset>で該当範囲を横に動かす。

svg filter2.png

<filter id="glitch" primitiveUnits="objectBoundingBox" x="-10%" y="0%" width="120%" height="100%">
  <!-- ...省略... -->

  <!-- スライスごとに横に動かす -->
  <feOffset in="blended" result="slice1" dx="0" dy="0" y="0%" height="30%"></feOffset>
  <feOffset in="blended" result="slice2" dx="0.01" dy="0" y="30%" height="4%"></feOffset>
  <feOffset in="blended" result="slice3" dx="0" dy="0" y="34%" height="26%"></feOffset>
  <feOffset in="blended" result="slice4" dx="-0.007" dy="0" y="60%" height="2%"></feOffset>
  <feOffset in="blended" result="slice5" dx="0" dy="0" y="62%" height="38%"></feOffset>
</filter>

マージ

最後に、スライスを1つの画像にまとめる必要がある。スライス同士は範囲が重複しないので、<feMerge>を使って単に重ね合わせればよい(<feBlend>を使ってもできるが、<feMerge>の方がコンパクトに書ける)。

<filter id="glitch" primitiveUnits="objectBoundingBox" x="-10%" y="0%" width="120%" height="100%">
  <!-- ...省略... -->

  <!-- すべてのスライスをマージ -->
  <feMerge>
    <feMergeNode in="slice1" />
    <feMergeNode in="slice2" />
    <feMergeNode in="slice3" />
    <feMergeNode in="slice4" />
    <feMergeNode in="slice5" />
  </feMerge>
</filter>

結果は?のようになる。

CodePenで開く

アニメーション

アニメーションといってもCSSアニメーションではなく、SVG独自のアニメーション機能を使用する。アニメーションは属性値に対して適用するものであり、適用したい要素の子要素として<animate>を追加する。

例えば2つ目のスライスの<feOffset>に対して?のように<animate>を追加すると、そのスライスが動くようになる。attributeNameは変化させたい属性の名前、durがアニメーションの時間、keytimesはタイミング(0から1の値)、valuesは各タイミングにおけるその属性の値である。グリッチエフェクトでは滑らかに移動してほしくないため、calcModediscreteにすることで飛び飛びに移動するようにしている。また、repeatCountindefiniteにすることで永久に繰り返すようにしている。

<filter id="glitch" primitiveUnits="objectBoundingBox" x="-10%" y="0%" width="120%" height="100%">
  <!-- ...省略... -->

  <!-- 2つ目のスライス -->
  <feOffset in="blended" result="slice2" dy="0" y="30%" height="4%">
    <animate attributeName="dx"
            values="0; -0.03; -0.06"
            keytimes="0; 0.2; 0.8"
            begin="0s"
            dur="3s"
            calcMode="discrete"
            repeatCount="indefinite" />
  </feOffset>

  <!-- ...省略... -->
</filter>

CodePenで開く

最後に、スライスの数や高さを調節し、スライスだけでなく色収差の<feOffset>にもアニメーションを追加してvalueskeytimesを調節すると完成である?。

CodePenで開く

注意点

今回作成したものはWindows 10のChrome 80.0.3987.87とFirefox 74.0b1とEdge 80.0.361.48、 AndroidのChrome 80.0.3987.87とFirefox 68.4.2で正常に表示されることを確認したが、IEや非ChromiumのEdgeでは正常に表示されなかった。また、ChromeやFirefoxでも(今回使用しなかった)一部のフィルターでバグが存在するため、十分に注意する必要がある。

最後に

SVGフィルターを用いることで、元の要素を複製するなどの変更をHTMLに加えなくても、フィルターを1つ適用するだけでグリッチエフェクトを実現することができる。また、フィルターの定義はページのどこからでも参照できるため、何度も定義する必要がない。

このようなエフェクトを作るとき、ほとんどの場合CSSで事足りるし、ブラウザーの対応状況が少し不安であるということもあり、SVGフィルターを使う機会はあまりないと思われるが、複雑なフィルターを作ることができるという点で利点があることを覚えておくとよいかもしれない。

参考


  1. Chrome 80で試した限りでは、あるSVGで定義されたフィルターがprimitiveUnits="userSpaceOnUse"であるとき、別のSVG内の要素にこのフィルター適用すると、フィルタープリミティブのx y width heightがパーセント値であるとき、その100%が後者のSVGのサイズではなく前者のSVGのサイズとなるというバグが発生する。Firefoxではこの現象は起きなかった。また、検索してもこのことに関する情報は見つからなかった。デモ: https://codepen.io/righteous_github/pen/ZEGzwJy 

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

CSSぴえんチャレンジ

CSSぴえんチャレンジって何?

正確には「HTMLとCSSでぴえんの絵文字を再現できるか」というチャレンジを勝手に作って勝手にやりました。

ぴえんの絵文字がわからない?チャレンジ結果をご覧ください。

チャレンジ結果

See the Pen pien by niko (@rorome15nikomu) on CodePen.

_人人人人人人人人人人_
> これがぴえんだ! <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y ̄

おわりに

皆さまも息抜きや力試しに#CSSぴえんチャレンジやってみてはいかがでしょうか?

もっとクオリティの高いぴえんができたら#CSSぴえんチャレンジでツイートしてください!
全力でリツイート&いいねします。

私のTwitterはこちら

お付き合いいただきありがとうございました!

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

SwiftでQiitaのトレンドを取得して表示させる

QiitaのAPIにはトレンドを取得するAPIが存在しない

現在、SwiftUIでQiitaの記事を参照することができるクライアントアプリを作成中です。
トレンドの記事をリスト化して表示できたらいいなと思っていたらまさかのAPI自体提供されていない...
他のQiitaの記事でも同じようなことを実現させようと新たにQiitaのトレンドAPIを非公式で公開してるエンジニアも...

今回iOSネイティブアプリを作成しているので、Swiftだけで解決できないか試してみました!

SwiftSoup

Qiitaのトップページを解析する

import SwiftSoup
import Combine

// Qiitaのページ(1日)
let urlString = "https://qiita.com/"
// 週間: https://qiita.com/?scope=weekly
// 月間: https://qiita.com/?scope=monthly

// タスクの初期化
var task = : AnyCancellable?
// URLに変換
let url = URL(string: urlString)!

// HTMLを取得
task = URLSession.shared.dataTaskPublisher(for: url)
    .receive(on: DispatchQueue.main)
    .sink(receiveCompletion: { completion in
        switch completion {
        case .finished:
            print("成功")
        case let .failure(error):
            print(error)
        }
    }, receiveValue: { data, _ in
        do {
            let text = String(data: data, encoding: .utf8)!

            // ここからSwiftSoupでパース
            let doc: Document = try SwiftSoup.parse(text)
            let link: Element = try doc.select("div[data-hyperapp-app=Trend]").first()!
            let json: Data = try link.attr("data-hyperapp-props").data(using: .utf8)!

            // Codableでデコード
            let response = try JSONDecoder()
                .decode(TrendResponse.self,
                        from: json)

            /// edgesにトレンドの記事が格納される
            print(response.trend.edges)
        } catch {
            print("エラー")
        }
    })

Codable

struct TrendResponse: Codable {

    let scope: String
    var trend: Trend

}

struct Trend: Codable {

    let edges: [Edges]

}

struct Edges: Codable {

    let followingLikers: [Author]
    let isLikedByViewer: Bool
    let isNewArrival: Bool
    let hasCodeBlock: Bool
    let node: Node

}

struct Node: Codable {

    let createdAt: String
    let likesCount: Int
    let title: String
    let uuid: String
    let author: Author

}

struct Author: Codable {

    let profileImageUrl: String
    let urlName: String

}

完成した画面

まとめ

  • SwiftでHTMLを取得しパースすることで実現することができた。
  • 現在、SwiftUIでクライアントアプリを絶賛開発中です。
  • TestFlightでテスト配信もしているので、ご興味がある方は、コメントまたは連絡ください!

参考記事

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

ScrollReveal+CSSアニメーションでWebページをちょっとだけオシャレにしてみる

この記事は「納期1日だけどオシャレなアニメーションもつけてイイ感じにして:innocent:」と言われた人間が、頑張って工夫したことをまとめたものです。

タイトルにあるScrollRevealとは

ScrollRevealはスクロールアニメーション系のJavascriptライブラリです。
jQuery不要、CSS不要で手軽にスクロールアニメーションを実装できます。

事前準備

まずHTMLでscrollreveal.jsを読み込みます。

HTML
<script src="/js/scrollreveal.min.js"></script>

そして、アニメーションさせたい要素に任意のclassを設定します。

HTML
<div class="anime">アニメーションさせたい要素</div>

ここではclass="anime"を使うことにしました。

やりたいこと

  • ページをスクロールすると要素がフワっと出てくる
  • 要素がフワッと出てきた後で、要素内のアイコンが動く

やりたいこと① ページをスクロールすると要素がフワっと出てくる

「事前準備」で用意したアニメーションさせたい要素に対してアニメーションを実装します。

Javascript
ScrollReveal().reveal('.anime');

1行書いただけ。お手軽です。

もう少し動きを変えたい場合はオプションも設定できます。

Javascript
ScrollReveal().reveal('.anime',{
    duration: 1000, //アニメーションの長さ
    delay: 500      //アニメーションの遅延
});

試しに動かすとこんな感じ↓


See the Pen
ScrollReveal-sample01
by tricolorebox (@tricolorebox)
on CodePen.


やりたいこと② 要素がフワッと出てきた後で、要素内のアイコンが動く

さて、こまった!

スクロールで要素が出てきた後で、アクセントとしてアイコン画像を動かしたいのですが(※イメージとしてはauトップページ)細かいアニメーションには対応していません:disappointed_relieved:

アイコンのアニメーションをよく観察すると...?

アイコンは背景画像として読み込まれているようです。
CSSアニメーションでアイコンのスプライト画像をパラパラ漫画のように1コマずつ動かしていました。これを組み合わせられないかな:thinking:

アニメーション終了に任意のclassを付与する

ここで先ほどのScrollRevealの話に戻ります。
ScrollRevealではafterRevealというオプションでコールバックを設定できます。
これにより、アニメーション後に任意の関数を実行することが可能です。
これを利用してアニメーションが終わったら、is-visibleというclassを付与します。

Javascript
ScrollReveal().reveal('.anime',{
    afterReveal: function (el){
        el.classList.add('is-visible');
    }
});

このような書き方になります。

CSSでアイコン用のアニメーションを設定する

最後にアイコン用のアニメーションを設定します。
繋がったアイコン画像を読み込んでアニメーションを設定します。
今回は1コマ96x96として、用意したのはこんな感じのイラスト↓
sample_anim.png

そしてCSSでis-visibleのclassがついた要素の子要素にアイコンのアニメーションを設定します。

HTML
<div class="anime">
    <div class="icon"></div>
    <div class="inner">アニメーションさせたい要素</div>
</div>
CSS
.icon{
    background:url(/img/sample_anim.png) no-repeat 0 0;
    width:96px;
    height:96px;
    margin:0 auto;
}
.is-visible .icon{
    animation: parapara 1s steps(8) 3;
    /*1秒間8コマのアニメーションを3回繰り返す*/
}
@keyframes parapara {
  to {
    background-position: -768px 0;
    /*アイコン幅96x8コマ=768px*/
  }
}

どうでしょうか、ちょっと可愛くなったかな...

See the Pen ScrollReveal-sample02 by tricolorebox (@tricolorebox) on CodePen.

最後に

アニメーション部分だけのまとめになりましたが、実際お仕事ではスマホ用・PC用で別々にスクロールアニメーションを設定しました。
アニメーションのタイミング等があとで調整しやすいライブラリを選べると自分も楽できますよ:v_tone2:

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

YouTubeの字幕機能の限界に迫る

YouTubeの字幕機能の限界に迫る

YouTubeには、字幕機能というものがある。

一般的にこの字幕機能では、表示をONにすると通常黒い背景に白い文字が浮かび上がってくる。(視聴者が多少表示設定を変更することもできる。)

Screenshot_2020-02-10 【紫咲シオン】難しい話をしてしまいハーバード大学卒であることがバレてしまうシオン.png

筆者も今までに膨大な数の動画を視聴してきたが、先日初めて画期的な字幕を使用している動画を発見した。


Screenshot_2020-02-10 にじさんじ(1).png

にじさんじ - Virtual to LIVE [Official Music Video]


画面上部および下部に背景がなく文字がカラフルな歌詞が表示されている。
しかしこれは動画として埋め込まれている文字(映像)ではない。
現に字幕表示をOFFにすると歌詞も表示されなくなる。
Screenshot_2020-02-10 にじさんじ(2).png

そして、実際の動画で確認してほしいのだが、驚くべきことtにカラオケ字幕になっている(歌ったところの文字が変わる)。

というわけでこの字幕のシステムが意味不明すぎたので調べてみたところ、yttファイルというものを使っているのだろうということが判明した。

YouTubeの字幕仕様

字幕をつける方法

そもそもYouTubeにおいて動画に字幕をつけるのには、大別して二通りの方法がある。

  • 文字起こし機能を使う
  • ファイルをアップロードする

この二つである。

おそらくほとんどの動画は前者である文字起こし機能を利用して作成されている。
文字起こし機能は、ブラウザ上で動作するGUIの字幕製作エディタである。
しかしこの文字起こし機能では必要最低限の機能しか使えない。具体的にいうと

  • 文字色は白固定
  • 背景色は黒固定
  • フォント固定
  • サイズ固定
  • 画面上同時に表示できる字幕は1つまで

といったものである。
もちろん動画に字幕をつけるだけであればこれだけで十分だが、先ほど紹介した動画のような字幕は到底作成できない。

まあつまり、件の動画は字幕ファイルをアップロードして作成されている、ということが確実にいえる。

ファイル形式

YouTubeの公式ヘルプにあるYouTubeが対応していると明記されている字幕ファイル形式について実際に利用してみた。

ファイル形式 拡張子 スタイリング対応 実用性
SubRip .srt
SubViewer .sbv/.sub -
MPsub .mpsub - △(?)
LRC .lrc - △(?)
Videotron Lambda .cap - △(?)
SAMI .smi/.sami 色太イ下影
RealText .rt - ×
WebVTT .vtt 複太イ下座
TTML .ttml
DFXP .ttml/.dfxp
テレビ用字幕形式各種 - -

注:
  複:同時複数箇所表示可能
  色:文字色指定可能
  太:太字指定可能
  イ:イタリック指定可能
  下:下線付き文字指定可能
  座:表示座標指定可能
  影:影付き文字指定可能
  フ:フォント利用可能
  背:背景色変更可能
  サ:文字サイズ指定可能

みてもらえればわかると思うが、なんと全機能を利用できる形式がない。そもそもRealTextのように対応していると書いてあるのにアップロードすらできないファイル形式もあった。
また、すべての形式を見ても、フォント、背景色、文字サイズなどは指定ができない
しかし実際には件の動画のように表示ができるわけだ。
これはいったいどういうことなのか?

詳しく調べたところ、YouTubeの内部仕様には、すべての機能を使用可能なTTMLをベースに構築されたオリジナルの字幕ファイル形式があるようだということがわかった。

以降はこのオリジナルの形式をYTT形式(YouTube Timed Text)と呼ぶことにする。

ファイル形式 拡張子 スタイリング対応 実用性
YTT .ytt 複色太イ下座影フ背サ

YTT形式の仕様

ここまでがめちゃめちゃ長かったが、このYTT形式の仕様を解説したい。
まず前提として、YouTubeはYTT形式の存在をサポート等の情報としてどこにも明記していない。
また、YTTの詳細な記法についても、日本語・英語のサイトではまったく見つけられなかった。
見つかったのは、YTTファイルが存在するということと、そのYTTファイルの例だけである。
今回の記事はそれをまとめたものなので、実はもっと機能があるかもしれない。

ファイル様式

YTTは、前述したようにTTMLをベースに構築された字幕用マークアップ言語である。
そしてそのベースになったTTMLは、XMLをベースに字幕表示をサポートするために構築された言語だ。
つまり、YTTファイルはXMLベースである。
この先の記事を読む場合、XMLを知っている人であることが望ましい。

YTTファイルの拡張子は .ytt である。
また、テキストコーデックはUTF-8だ。

YTT記法

まあhtmlと一緒なので雑に書く。

お約束

お約束です。先頭に書く。

お約束
<?xml version="1.0" encoding="utf-8" ?>
<timedtext format="3">
   <head>
    <!--ヘッダ-->
   </head>
   <body>
    <!--ボディ-->
   </body>
</timedtext>

ヘッダ

YTTではスタイリング情報はすべてヘッダに記述する。
また、head内に記述されたすべてのエレメントはidを紐付けなくてはならない。

まずは例をみてみよう。

example.ytt
<?xml version="1.0" encoding="utf-8" ?>
<timedtext format="3">
<head>
  <!--文字情報-->
    <pen id="1" b="1"/> <!--太字-->
    <pen id="2" fc="#FF0000" fo="40"  bo="0" /> <!--色と不透明度-->
  <!--座標情報-->
    <wp id="0"  ap="0" ah="0"   av="0" /> <!--左上-->
    <wp id="1"  ap="4" ah="50"   av="50" /> <!--ど真ん中-->
    <wp id="2"  ap="8" ah="100"   av="100" /> <!--右下-->
</head>
</timedtext>

例にあるように、head内にpenやwpというエレメントを作成し、その各エレメントにIDを付与することで、bodyからidを指定することでスタイルを指定できる。言ってしまえばスタイルシートである。

各エレメントについて解説する


penエレメント
<pen />

最も基本的なエレメント。
文字や背景自体の装飾をするときに使用する。
引数に指定するものによって効果が変わる

引数 効果
id="int" 必須
b="1" 太字になる
i="1" イタリックになる
u="1" 下線付き文字になる
fc="#FFFFFF" 文字色の指定(カラーコード)
fo="int(0~255)" 文字の不透明度
bc="#000000" 背景色の指定(カラーコード)
bo="int(0~255)" 背景の不透明度
ec="#FF0000" 影・縁色の指定(カラーコード)
et="int(1~4)" 影・縁のタイプ(スマホ非対応)
fs="int(0~7)" フォント(スマホ非対応)
fs="int" フォントサイズ(Android非対応)
rb="int(1,4,5)" ルビ
of="int(0~2)" 上・下付き文字
te="int(1~2)" Text emphasis
hg="1" Text combinations

フォントサイズ計算式: 100 + (fs - 100) / 4 (%)


wsエレメント
<ws />

文字記法エレメント。
縦書き横書きの変更。
引数に指定するものによって効果が変わる

引数 効果
ju="int(1~2)" 水平方向のテキストの配置
pd="int(1~3)" 縦書きでの文字回転
sd="int(1~3)" 縦書きでの回転角

wpエレメント
<wp />

文字座標エレメント。
文字を書く座標を指定する。
引数に指定するものによって効果が変わる

引数 効果
ap="int(0~8)" アンカーポイント
ah="int(0~100)" X座標
av="int(0~100)" Y座標

アンカーポイント↓
____
|0 1 2|
|3 4 5|
|6 7 8|
ーーーー

ボディ

まずは例をみてみよう。

example.ytt
<?xml version="1.0" encoding="utf-8" ?>
<timedtext format="3">
<head>
  <!--文字情報-->
    <pen id="1" b="1"/> <!--太字-->
    <pen id="2" fc="#FF0000" fo="40"  bo="0" /> <!--色と不透明度-->
  <!--座標情報-->
    <wp id="0"  ap="0" ah="0"   av="0" /> <!--左上-->
    <wp id="1"  ap="4" ah="50"   av="50" /> <!--ど真ん中-->
    <wp id="2"  ap="8" ah="100"   av="100" /> <!--右下-->
</head>
<body>
   <p t="0" d="4000" wp="1" p="1">Bold</p>
   <p t="4000" d="4000" wp="0" p="2">左上</p>
   <p t="4000" d="4000" wp="2" p="3">右下</p>
</body>
</timedtext>

bodyに関してはとてもシンプルだ。
pやsで囲って、そこに引数として表示開始時間、表示する長さ、読み込むpen、読み込むws、読み込むwpを指定するだけである。

使えるエレメント 意味
p Paragraph
s Span
引数 効果
t="int" 表示開始時刻(動画開始からのミリ秒)
d="int" 表示する長さ(ミリ秒)
p="int(id)" headで指定したpenを読み込む
ws="int(id)" headで指定したwsを読み込む
wp="int(id)" headで指定したwpを読み込む

件の動画を再現してみる

限界に迫る、ということで件の動画の字幕を一部だけでも再現してみたい。
最初はyttファイル自体をダウンロードしたりできないかなぁと思っていたのだが、さすがに無理だったようで...
しかしXMLファイルはダウンロードできた。YouTubeの文字起こし等からは表示時間が整数でしかわからないがこのXMLファイルではミリ秒まで表記されているのでこれを元に再現する。
http://video.google.com/timedtext?hl=ja&lang=ja&v=zPMWAj54Seg

以下が完成した動画(最初の2フレーズのみだが)。
https://youtu.be/6dl416-x1ig

そしてこれがコード

nijisanji.ytt
<?xml version="1.0" encoding="utf-8" ?>
<timedtext format="3">
<head>
<pen id="1" ec="#B22222" fc="#FEFEFE" fo="255" bo="0" b="1" et="3"/>
<pen id="2" ec="#FFA07A" fc="#FEFEFE" fo="255" bo="0" b="1" et="3"/>
<pen id="3" ec="#FFFF50" fc="#FEFEFE" fo="255" bo="0"  b="1" et="3"/>
<pen id="4" ec="#FFDAB9" fc="#FEFEFE" fo="255" bo="0"  b="1" et="31"/>
<pen id="5" ec="#DDFF50" fc="#FEFEFE" fo="255" bo="0"  b="1" et="3"/>
<pen id="6" ec="#90EE90" fc="#FEFEFE" fo="255" bo="0"  b="1" et="3"/>
<pen id="7" ec="#00FF7F" fc="#FEFEFE" fo="255" bo="0"  b="1" et="3"/>
<pen id="8" ec="#7FFFD4" fc="#FEFEFE" fo="255" bo="0"  b="1" et="3"/>
<pen id="9" ec="#87CEEB" fc="#FEFEFE" fo="255" bo="0"  b="1" et="3"/>
<pen id="10" ec="#6495ED" fc="#FEFEFE" fo="255" bo="0" b="1"  et="3"/>
<pen id="11" ec="#DA70D6" fc="#FEFEFE" fo="255" bo="0"  b="1" et="3"/>
<pen id="12" ec="#FF50FF" fc="#FEFEFE" fo="255" bo="0"  b="1" et="3"/>

<pen id="20" b="1" bo="0" />
<pen id="21" bo="0" />
<pen id="22" bo="0" />

<pen id="30" fc="#FEFEFE" fo="200" bo="0" />
<pen id="31" fc="#686A7A" fo="255" bo="0" />

<wp id="1" ap="4" ah="50" av="90" />

</head>
<body>
<p t="5413" d="467" p="20" wp="1"><s p="1" >どう</s><s p="2" ></s><s p="3" ></s><s p="4" ></s><s p="5" ></s><s p="6" ></s><s p="7" >く今</s><s p="8" ><s p="9" ></s><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="5413" d="467" p="21" wp="1"><s p="30">どうしようもなく今を生きてる</s></p>

<p t="5880" d="333" p="20" wp="1"><s p="1" >どう</s><s p="2" ></s><s p="3" ></s><s p="4" ></s><s p="5" ></s><s p="6" ></s><s p="7" >く今</s><s p="8" ><s p="9" ></s><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="5880" d="333" p="21" wp="1"><s p="31">どうし</s><s p="30">ようもなく今を生きてる</s></p>

<p t="6213" d="401" p="20" wp="1"><s p="1" >どう</s><s p="2" ></s><s p="3" ></s><s p="4" ></s><s p="5" ></s><s p="6" ></s><s p="7" >く今</s><s p="8" ><s p="9" ></s><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="6213" d="401" p="21" wp="1"><s p="31">どうしよう</s><s p="30">もなく今を生きてる</s></p>

<p t="6614" d="167" p="20" wp="1"><s p="1" >どう</s><s p="2" ></s><s p="3" ></s><s p="4" ></s><s p="5" ></s><s p="6" ></s><s p="7" >く今</s><s p="8" ><s p="9" ></s><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="6614" d="167" p="21" wp="1"><s p="31">どうしようも</s><s p="30">なく今を生きてる</s></p>

<p t="6781" d="200" p="20" wp="1"><s p="1" >どう</s><s p="2" ></s><s p="3" ></s><s p="4" ></s><s p="5" ></s><s p="6" ></s><s p="7" >く今</s><s p="8" ><s p="9" ></s><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="6781" d="200" p="21" wp="1"><s p="31">どうしようもな</s><s p="30">く今を生きてる</s></p>

<p t="6981" d="400" p="20" wp="1"><s p="1" >どう</s><s p="2" ></s><s p="3" ></s><s p="4" ></s><s p="5" ></s><s p="6" ></s><s p="7" >く今</s><s p="8" ><s p="9" ></s><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="6981" d="400" p="21" wp="1"><s p="31">どうしようもなく</s><s p="30">今を生きてる</s></p>

<p t="7381" d="401" p="20" wp="1"><s p="1" >どう</s><s p="2" ></s><s p="3" ></s><s p="4" ></s><s p="5" ></s><s p="6" ></s><s p="7" >く今</s><s p="8" ><s p="9" ></s><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="7381" d="401" p="21" wp="1"><s p="31">どうしようもなく今</s><s p="30">を生きてる</s></p>

<p t="7782" d="200" p="20" wp="1"><s p="1" >どう</s><s p="2" ></s><s p="3" ></s><s p="4" ></s><s p="5" ></s><s p="6" ></s><s p="7" >く今</s><s p="8" ><s p="9" ></s><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="7782" d="200" p="21" wp="1"><s p="31">どうしようもなく今を</s><s p="30">生きてる</s></p>

<p t="7982" d="167" p="20" wp="1"><s p="1" >どう</s><s p="2" ></s><s p="3" ></s><s p="4" ></s><s p="5" ></s><s p="6" ></s><s p="7" >く今</s><s p="8" ><s p="9" ></s><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="7982" d="167" p="21" wp="1"><s p="31">どうしようもなく今を生</s><s p="30">きてる</s></p>

<p t="8149" d="200" p="20" wp="1"><s p="1" >どう</s><s p="2" ></s><s p="3" ></s><s p="4" ></s><s p="5" ></s><s p="6" ></s><s p="7" >く今</s><s p="8" ><s p="9" ></s><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="8149" d="200" p="21" wp="1"><s p="31">どうしようもなく今を生き</s><s p="30">てる</s></p>

<p t="8349" d="200" p="20" wp="1"><s p="1" >どう</s><s p="2" ></s><s p="3" ></s><s p="4" ></s><s p="5" ></s><s p="6" ></s><s p="7" >く今</s><s p="8" ><s p="9" ></s><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="8349" d="200" p="21" wp="1"><s p="31">どうしようもなく今を生きて</s><s p="30"></s></p>

<p t="8549" d="567" p="20" wp="1"><s p="1" >どう</s><s p="2" ></s><s p="3" ></s><s p="4" ></s><s p="5" ></s><s p="6" ></s><s p="7" >く今</s><s p="8" ><s p="9" ></s><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="8549" d="567" p="21" wp="1"><s p="31">どうしようもなく今を生きてる</s></p>

<p t="9116" d="201" p="20" wp="1"><s p="1" ></s><s p="2" ></s><s p="3" ></s><s p="5" ></s><s p="7" ></s><s p="8" ><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="9116" d="201" p="21" wp="1"><s p="31"></s><s p="30">の声が届く未来が</s></p>

<p t="9317" d="400" p="20" wp="1"><s p="1" ></s><s p="2" ></s><s p="3" ></s><s p="5" ></s><s p="7" ></s><s p="8" ><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="9317" d="400" p="21" wp="1"><s p="31">この</s><s p="30">声が届く未来が</s></p>

<p t="9717" d="363" p="20" wp="1"><s p="1" ></s><s p="2" ></s><s p="3" ></s><s p="5" ></s><s p="7" ></s><s p="8" ><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="9717" d="363" p="21" wp="1"><s p="31">この声</s><s p="30">が届く未来が</s></p>

<p t="10084" d="400" p="20" wp="1"><s p="1" ></s><s p="2" ></s><s p="3" ></s><s p="5" ></s><s p="7" ></s><s p="8" ><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="10084" d="400" p="21" wp="1"><s p="31">この声が</s><s p="30">届く未来が</s></p>

<p t="10484" d="367" p="20" wp="1"><s p="1" ></s><s p="2" ></s><s p="3" ></s><s p="5" ></s><s p="7" ></s><s p="8" ><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="10484" d="367" p="21" wp="1"><s p="31">この声が届</s><s p="30">く未来が</s></p>

<p t="10851" d="201" p="20" wp="1"><s p="1" ></s><s p="2" ></s><s p="3" ></s><s p="5" ></s><s p="7" ></s><s p="8" ><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="10851" d="201" p="21" wp="1"><s p="31">この声が届く</s><s p="30">未来が</s></p>

<p t="11052" d="200" p="20" wp="1"><s p="1" ></s><s p="2" ></s><s p="3" ></s><s p="5" ></s><s p="7" ></s><s p="8" ><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="11052" d="200" p="21" wp="1"><s p="31">この声が届く未</s><s p="30">来が</s></p>

<p t="11252" d="367" p="20" wp="1"><s p="1" ></s><s p="2" ></s><s p="3" ></s><s p="5" ></s><s p="7" ></s><s p="8" ><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="11252" d="367" p="21" wp="1"><s p="31">この声が届く未来</s><s p="30"></s></p>

<p t="11619" d="567" p="20" wp="1"><s p="1" ></s><s p="2" ></s><s p="3" ></s><s p="5" ></s><s p="7" ></s><s p="8" ><s p="10" ></s><s p="11" ></s><s p="12" ></s></p>
<p t="11619" d="567" p="21" wp="1"><s p="31">この声が届く未来が</s></p>



</body>
</timedtext>

手動だとめちゃめちゃ大変である。

最後に

たぶんめちゃめちゃ読みづらかったと思うけど読んでくれてありがとうございます。
yttデータはYouTubeの字幕の中で最も応用性が高い、というかまともに使えるのはこれしかないのに文献がまったくないので記事にしてみました。
参考にしたファイルの全文を以下においておきます。

ytt.ytt
<?xml version="1.0" encoding="utf-8" ?>
<timedtext format="3">
<head>
    <!-- The elements in the <head> *must* appear in order of increasing ID's or the server will renumber them and screw everything up. -->

    <!-- Text styles -->
    <pen id="1" b="1" />            <!-- Bold       -->
    <pen id="2" i="1" />            <!-- Italic     -->
    <pen id="3" u="1" />            <!-- Underline  -->

    <!-- Foreground color/opacity. The iOS app supports both, while the Android app only supports color. -->
    <pen id="4" fc="#FF0000" fo="40"  bo="0" />
    <pen id="5" fc="#00FF00" fo="127" bo="0" />
    <pen id="6" fc="#0000FF" fo="255" bo="0" />             <!-- fo="255" is the default and will get removed after uploading,          -->
                                                            <!-- meaning the viewer's custom opacity setting (if any) will take         -->
                                                            <!-- effect. Because most viewers set this custom opacity by accident       -->
                                                            <!-- rather than intentionally (all it takes is to press O), it's           -->
                                                            <!-- recommended to go no higher than fo="254", thereby always              -->
                                                            <!-- overruling the custom setting and forcing subtitles to be readable.    -->

    <!-- Background color/opacity. In theory, the iOS app supports both, but only if the        -->
    <!-- well-hidden "Video Override" option is turned on in the iOS accessibility settings.    -->
    <!-- In practice, neither Android nor iOS support custom backgrounds.                       -->
    <pen id="7"  fc="#FEFEFE" bc="#FF0000" bo="40"  />
    <pen id="8"  fc="#FEFEFE" bc="#00FF00" bo="127" />
    <pen id="9"  fc="#FEFEFE" bc="#0000FF" bo="255" />
    <pen id="10" fc="#000000" bc="#FFFFFF" bo="255" />      <!-- In practice, bo should be limited to 254 for the same reason as fo.    -->

    <!-- Edge color/type (not supported on mobile)  -->
    <pen id="11" fc="#FEFEFE" ec="#FF0000" et="1" />        <!-- Hard shadow    -->
    <pen id="12" fc="#FEFEFE" ec="#00FF00" et="2" />        <!-- Bevel          -->
    <pen id="13" fc="#FEFEFE" ec="#0000FF" et="3" />        <!-- Glow/Outline   -->
    <pen id="14" fc="#FEFEFE" ec="#FF00FF" et="4" />        <!-- Soft shadow    -->

    <!-- Font styles (not supported on mobile)      -->
    <pen id="15" fc="#FEFEFE" fs="0" />                     <!-- Default (same as 4).                               -->
    <pen id="16" fc="#FEFEFE" fs="1" />                     <!-- Monospaced Serif           (Courier New)           -->
    <pen id="17" fc="#FEFEFE" fs="2" />                     <!-- Proportional Serif         (Times New Roman)       -->
    <pen id="18" fc="#FEFEFE" fs="3" />                     <!-- Monospaced Sans-Serif      (Deja Vu Sans Mono)     -->
    <pen id="19" fc="#FEFEFE" fs="4" />                     <!-- Proportional Sans-Serif    (Roboto)                -->
    <pen id="20" fc="#FEFEFE" fs="5" />                     <!-- Casual                     (Comic Saaans!)         -->
    <pen id="21" fc="#FEFEFE" fs="6" />                     <!-- Cursive                    (Monotype Corsiva)      -->
    <pen id="22" fc="#FEFEFE" fs="7" />                     <!-- Small Capitals             (Carrois Gothic SC)     -->

    <!-- Font sizes (supported on iOS but not Android)                              -->
    <!-- The value is a virtual percentage of the default size; the real            -->
    <!-- percentage is 100 + (sz - 100) / 4, meaning sz="200" will result           -->
    <!-- in text that's *not* twice as big as the default but only 25% bigger.      -->
    <!-- The values can't be negative, meaning the smallest you can go is a         -->
    <!-- virtual percentage of 0 which equates to a real percentage of 75.          -->
    <pen id="23" sz="30"  />
    <pen id="24" sz="100" />        <!-- Default -->
    <pen id="25" sz="300" />

    <!-- Ruby (not supported on mobile) -->
    <pen id="26" rb="1" />              <!-- Ruby enable (for kanji spans)      -->
    <pen id="27" rb="4" />              <!-- Ruby over   (for furigana spans)   -->
    <pen id="28" rb="5" />              <!-- Ruby under  (for furigana spans)   -->

    <!-- Subscript/superscript ("of" = "offset"). Not supported on mobile -->
    <pen id="29" fc="#FEFEFE" of="0" />              <!-- Subscript      -->
    <pen id="30" fc="#FEFEFE" of="1" />              <!-- Regular        -->
    <pen id="31" fc="#FEFEFE" of="2" />              <!-- Superscript    -->

    <!-- Text emphasis (dots above text; not currently allowed by YouTube subtitle upload) -->
    <pen id="32" fc="#FEFEFE" te="1" />     <!-- Allows bold text       -->
    <pen id="33" fc="#FEFEFE" te="2" />     <!-- Disallows bold text    -->

    <!-- Text combination (\ytpack in YTSubConverter). Not supported on mobile -->
    <pen id="34" fc="#FEFEFE" hg="1" />

    <!-- Horizontal text alignment -->
    <ws id="1" ju="0" />                <!-- Left   -->
    <ws id="2" ju="1" />                <!-- Right  -->
    <ws id="3" ju="2" />                <!-- Center -->

    <!-- Vertical text alignment (not supported on mobile) --> 
    <ws id="4" pd="2" sd="0" />         <!-- Characters positioned above each other but not rotated; columns going from right to left   -->
    <ws id="5" pd="2" sd="1" />         <!-- Same as above, but columns going from left to right                                        -->
    <ws id="6" pd="3" sd="0" />         <!-- Entire subtitle rotated 90° CCW, columns going from left to right                          -->
    <ws id="7" pd="3" sd="1" />         <!-- Same as above, but columns going from right to left                                        -->
    <!-- pd="1" sets the paragraph to RTL mode, making the spans appear horizontally but in the opposite order. -->

    <!-- Positions (ap: anchor point, ah: X coordinate (0 = left, 100 = right), av: Y coordinate (0 = top, 100 = bottom) -->
    <wp id="0"  ap="0" ah="0"   av="10" />     <!-- "ap" determines which point on the subtitle box gets placed at the specified position:             -->
    <wp id="1"  ap="0" ah="0"   av="10" />     <!--   0 ======== 1 ======== 2                                                                          -->
    <wp id="2"  ap="1" ah="50"  av="10" />     <!--   |                     |                                                                          -->
    <wp id="3"  ap="2" ah="100" av="10" />     <!--   3          4          5                                                                          -->
    <wp id="4"  ap="3" ah="0"   av="50" />     <!--   |                     |                                                                          -->
    <wp id="5"  ap="4" ah="50"  av="50" />     <!--   6 ======== 7 ======== 8                                                                          -->
    <wp id="6"  ap="5" ah="100" av="50" />
    <wp id="7"  ap="6" ah="0"   av="90" />     <!-- The server only accepts integers for "ah" and "av", even though the player accepts floating point. -->
    <wp id="8"  ap="7" ah="50"  av="90" />     <!-- In theater mode, the width covered by "ah" includes the black bars on the sides, meaning the       -->
    <wp id="9"  ap="8" ah="100" av="90" />     <!-- subtitles move towards the sides and even out of the video.                                        -->
    <wp id="10" ap="1" ah="50"  av="70" />     <!-- The player transforms the coordinates according to effectiveCoord = (specifiedCoord * 0.96) + 2,   -->
</head>                                        <!-- meaning subtitles don't appear *quite* where you want them to.                                     -->
<body>
    <!-- Text styles -->
    <p t="0" d="4000" wp="1" p="1">Bold</p>             <!-- t = start time in ms, d = duration in ms, wp = position ID, p = pen ID.                -->
    <p t="0" d="4000" wp="2" p="2">Italic</p>           <!-- t="0" should be avoided as it causes the Android app to ignore positioning             -->
    <p t="0" d="4000" wp="3" p="3">Underline</p>        <!-- and sometimes not display the subtitle at all. (t="1" works fine, however)             -->
    <p t="0" d="4000" wp="5"><s p="1">Bold</s> <s p="2">Italic</s> <s p="3">Underline</s></p>

    <!--
        Usage of multiple spans has a bug: unless there's *some* text that's not part of any span, the server will remove
        the first span's "p" attribute, causing it to lose its formatting. Completely random, but that's how it is.
        The best workaround is to place a zero-width space after the first span, as this way there are no visual
        changes to the subtitle. (This workaround is used in all of the following examples)
     -->

    <!-- Foreground color/transparency -->
    <p t="4000" d="4000" wp="1" p="4">Red (a = 40)</p>
    <p t="4000" d="4000" wp="2" p="5">Green (a = 127)</p>
    <p t="4000" d="4000" wp="3" p="6">Blue (a = 255)</p>
    <p t="4000" d="4000" wp="5"><s p="4">Red </s>&#8203;<s p="5">Green </s><s p="6">Blue</s></p>

    <!-- Background color/transparency -->
    <p t="8000" d="4000" wp="1" p="7">Red (a = 40)</p>
    <p t="8000" d="4000" wp="2" p="8">Green (a = 127)</p>
    <p t="8000" d="4000" wp="3" p="9">Blue (a = 255)</p>
    <p t="8000" d="4000" wp="10" p="10">Opaque</p>
    <p t="8000" d="4000" wp="8"><s p="7">Red </s>&#8203;<s p="8">Green </s><s p="9">Blue</s></p>

    <!-- Edges -->
    <p t="12000" d="4000" wp="1" p="11">Edge type 1</p>
    <p t="12000" d="4000" wp="2" p="12">Edge type 2</p>
    <p t="12000" d="4000" wp="3" p="13">Edge type 3</p>
    <p t="12000" d="4000" wp="4" p="14">Edge type 4</p>
    <p t="12000" d="4000" wp="5"><s p="11">One </s>&#8203;<s p="12">Two </s><s p="13">Three </s><s p="14">Four</s></p>

    <!-- Fonts (one per paragraph) -->
    <p t="16000" d="4000" wp="1" p="15">Font 0</p>
    <p t="16000" d="4000" wp="2" p="16">Font 1</p>
    <p t="16000" d="4000" wp="3" p="17">Font 2</p>
    <p t="16000" d="4000" wp="4" p="18">Font 3</p>
    <p t="16000" d="4000" wp="5" p="19">Font 4</p>
    <p t="16000" d="4000" wp="6" p="20">Font 5</p>
    <p t="16000" d="4000" wp="7" p="21">Font 6</p>
    <p t="16000" d="4000" wp="8" p="22">Font 7</p>

    <!-- Fonts (multiple) -->
    <p t="20000" d="4000" wp="5"><s p="15">Zero </s>&#8203;<s p="16">One </s><s p="17">Two </s><s p="18">Three </s><s p="19">Four </s><s p="20">Five </s><s p="21">Six </s><s p="22">Seven</s></p>

    <!-- Font sizes -->
    <p t="24000" d="4000" wp="1" p="23">30%</p>
    <p t="24000" d="4000" wp="2" p="24">100%</p>
    <p t="24000" d="4000" wp="3" p="25">300%</p>
    <p t="24000" d="4000" wp="5"><s p="23">30% </s>&#8203;<s p="24">100% </s><s p="25">300%</s></p>

    <!-- Positioning -->
    <p t="28000" d="4000" wp="1">Top left</p>
    <p t="28000" d="4000" wp="2">Top center</p>
    <p t="28000" d="4000" wp="3">Top right</p>
    <p t="28000" d="4000" wp="4">Middle left</p>
    <p t="28000" d="4000" wp="5">Middle center</p>
    <p t="28000" d="4000" wp="6">Middle right</p>
    <p t="28000" d="4000" wp="7">Bottom left</p>
    <p t="28000" d="4000" wp="8">Bottom center</p>
    <p t="28000" d="4000" wp="9">Bottom right</p>

    <!-- Alignment -->
    <p t="32000" d="4000" wp="4" ws="1">Left-
aligned line</p>
    <p t="32000" d="4000" wp="5" ws="3">Centered
line</p>
    <p t="32000" d="4000" wp="6" ws="2">Right-
aligned line</p>

    <!-- Karaoke. Each span's "t" attribute gives its relative appearance time in ms.   -->
    <p t="36000" d="4000" wp="5" p="15"><s>Ka</s><s t="1000">ra</s><s t="2000">o</s><s t="3000">ke</s></p>

    <!-- Line breaks at section start/end (note the missing rounded corners - YouTube bug) -->
    <p t="40000" d="4000" wp="4"><s p="11">Line </s>&#8203;<s p="12">break </s><s p="13">
at start </s><s p="14">of span</s></p>
    <p t="40000" d="4000" wp="5"><s p="11">Line </s>&#8203;<s p="12">break
in </s><s p="13">middle </s><s p="14">of span</s></p>
    <p t="40000" d="4000" wp="6"><s p="11">Line </s>&#8203;<s p="12">break
</s><s p="13">at end </s><s p="14">of span</s></p>

    <!--
        Ruby text works by creating four spans for each ruby'd word:
        * A span for the kanji text, referring to a pen with rb="1"
        * A span with an opening parenthesis (fallback for clients without ruby support)
        * A span with the furigana text, optionally referring to a pen with rb="4"/"6" (above kanji) or "5"/"7" (below kanji)
        * A span with a closing parenthesis
        The ruby text always uses the same formatting as the base text (i.e. the other attributes of the furigana pen are ignored).

        The mobile apps don't support ruby text. They'll display the below subtitle as 漢(かん)字(じ).
    -->
    <p t="44000" d="4000" wp="5"><s p="26"></s><s>(</s><s p="27">かん</s><s>)</s><s p="26"></s><s>(</s><s p="28"></s><s>)</s></p>

    <!-- Subscript/superscript -->
    <p t="48000" d="4000" wp="5"><s p="29">Subscript </s>&#8203;<s p="30">Regular </s><s p="31">Superscript</s></p>

    <!-- Vertical text -->
    <p t="52000" d="4000" wp="1" ws="4">Vertical
text (RTL)</p>
    <p t="52000" d="4000" wp="3" ws="5">Vertical
text (LTR)</p>
    <p t="52000" d="4000" wp="7" ws="7">Rotated
text (RTL)</p>
    <p t="52000" d="4000" wp="9" ws="6">Rotated
text (LTR)</p>
</body>
</timedtext>

参考文献

YTSubConverter - GitHub

  • Aegisubのデータをyttファイルに変換してくれるツール。

https://github.com/arcusmaximus/YTSubConverter

Timed Text - Wikimedia Commons

https://commons.wikimedia.org/wiki/Commons:Timed_Text

にじさんじ - Virtual to LIVE [Official Music Video]

https://www.youtube.com/watch?v=zPMWAj54Seg

XMLファイル - にじさんじ Virtual to LIVE

http://video.google.com/timedtext?hl=ja&lang=ja&v=zPMWAj54Seg

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

Bootstrapでレイアウトが崩れた時の対処法(縦一列から普通の横並びに直す)

Bootstrap使って編集しているが、レイアウトが上手く機能していない。ちゃんと掲示板を横から順に表示されて欲しいのに、なぜか縦一列に表示される。

理想
Screenshot from Gyazo

実際
Screenshot from Gyazo

対処法

余計な</div>タグがあったため、形が崩れていた。ちゃんとレイアウトの指定しているのに形が崩れていた場合、タグ数があっているか確認しましょう。

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

Bootstrapでレイアウトが崩れた時の対処法

Bootstrap使って編集しているが、レイアウトが上手く機能していない。ちゃんと掲示板を横から順に表示されて欲しいのに、なぜか縦一列に表示される。

理想
Screenshot from Gyazo

実際
Screenshot from Gyazo

対処法

余計な</div>タグがあったため、形が崩れていた。ちゃんとレイアウトの指定しているのに形が崩れていた場合、タグ数があっているか確認しましょう。

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

未経験から始めるHTML/CSS ~float解除編~

はじめに

2020年2月より未経験ながらHTML/CSSをProgateにて学習を開始し、
その中でHTML/CSSを学習するに当たって私自身が疑問に思ったことを纏めてみました。
今後お互いの学習に役立てれば良いと考えQiitaに投稿を始めました。
今回はその3回目、float解除について説明して行きます。

対象者

  • ProgateでHTML/CSSを学習中の方

自己紹介

こんにちは。
私は現在27歳で、4年間半導体業界でテストエンジニアとしてSierにて働いております。
この度半導体業界の不景気により仕事がない状態が続いており2月29日を持ちまして退職、
また脱Sierを目指し学生時代に興味があったweb関係の仕事に就こうと考えている者です。

その前にfloatとは?

floatの記事については前回書きましたので下記を参照下さい。
未経験から始めるHTML/CSS ~float編~

float解除の前に

float解除の説明前にこちらのソースを見てください。
こちらのプログラムは子要素のみにheightを適用し親要素にはheightを適用していません。
さてこのプログラムの実行結果はどうなるでしょうか。

index.html
<!DOCTYPE html>

<html lang="ja">
<head>
<meta charset="UTF-8">
<title>  float  </title>
<link rel="stylesheet" href="style.css">
</head>

 <body>
   <div class="parent">
    <div class="child"></div>
  </div>
 </body>

</html>
style.css
.parent{
  background-color: red;
}

.child{
  background-color: blue;
  width: 100px;
  height: 100px;
}

下記が実行結果です。親要素にheightを適用していませんが抱合の関係にあるので、
子要素に応じて親要素の高さが変動しています。
スクリーンショット 2020-02-11 9.09.44.png

では次にfloatを子要素に指定し親要素にheightを指定しなかったらどうなるでしょうか。

index.html
<!DOCTYPE html>

<html lang="ja">
<head>
<meta charset="UTF-8">
<title>  float  </title>
<link rel="stylesheet" href="style.css">
</head>

 <body>
   <div class="parent">
    <div class="child"></div>
  </div>
 </body>

</html>
style.css
.parent{
  background-color: red;
}

.child{
  background-color: blue;
  width: 100px;
  height: 100px;
  float: left;
}

下記が実行結果です。何と親要素が消えてしまいました。
実は、floatを子要素に指定し親要素にheightを指定しないと親要素の高さが0になリます。
では親要素に高さを認識させるにはどうしたら良いでしょうか?
そこで出てくるのがfloat解除です。

スクリーンショット 2020-02-11 9.22.05.png

float解除

前置きが長くなりましたがfloatを解除する3つの方法を説明します。

  1. float直後の要素にclear:bothを適用。
  2. 親要素にoverflow:hiddenを適用。
  3. 親要素にclearfixクラスを適用。

1. float直後の要素にclear:bothを適用

progateのHTML/CSSの単元で出てきますがclear:bothを使用するとそれ以降の要素は
floatの影響がなくなります。一番シンプルですが、人よっては毎回float直後の要素に
clear:bothを使用するのでソースが冗長的で美しくないと感じる方もいるでしょう。

index.html
<!DOCTYPE html>

<html lang="ja">
<head>
<meta charset="UTF-8">
<title>  float  </title>
<link rel="stylesheet" href="style.css">
</head>

 <body>
   <div class="parent">
    <div class="child"></div>
      <div class = "floatRelease"></div> <!--毎回書くとソースが冗長-->
  </div>
 </body>

</html>
style.css
.parent{
  background-color: red;
}

.child{
  background-color: blue;
  width: 100px;
  height: 100px;
  float: left;
}

.floatRelease{
  clear: both; 
}

2. 親要素にoverflow:hiddenを適用

clear:bothより冗長でないですが注意しないといけない点があるので個人的は推奨しません。
overflow:hiddenは子要素が親要素をはみ出した際に表示されないという欠点があるからです。
position: relativeとabsoluteを使用した際に注意が必要です。

index.html
<!DOCTYPE html>

<html lang="ja">
<head>
<meta charset="UTF-8">
<title>  float  </title>
<link rel="stylesheet" href="style.css">
</head>

 <body>
   <div class="parent">
    <div class="child"></div>
  </div>
 </body>

</html>
style.css
.parent{
  background-color: red;
  overflow:hidden; 
}

.child{
  background-color: blue;
  width: 100px;
  height: 100px;
  float: left;
}

3. 親要素にclearfixクラスを適用

上記2つとは違い特にデメリットがないので使い易いです。

index.html
<!DOCTYPE html>

<html lang="ja">
<head>
<meta charset="UTF-8">
<title>  float  </title>
<link rel="stylesheet" href="style.css">
</head>

 <body>
   <div class="parent clearfix">
    <div class="child"></div>
  </div>
 </body>

</html>
style.css
.clearfix::after {
  content: "";
  display: block;
  clear: both;
}

.parent{
  background-color: red;
}

.child{
  background-color: blue;
  width: 100px;
  height: 100px;
  float: left;
}

おわりに

Progateではfloatの解除はclear:bothが使用されいますが、
web上ではclearfixが多用されていたので解除方法にどういうものがあるか気になり調べました。
clearfixはデメリットがなさそうなので個人的には一番お勧めです。
またCSS3からはflexboxという新しいモジュールがあるので、煩わしいfloatを使わなくて良いので
興味がありましたら調べてみてください。
私の経験が皆さんの役に立てれば幸いです。

Twitterもやっています!
もし興味が湧きましたらフォローよろしくお願いします。

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

オリジナルARマーカーを作る

やること

オリジナルのARマーカーを作ってARで遊びます

参考サイト

こちらのサイトを参考にさせて頂きました。

ARのサンプルを用意

ARではおなじみHiroのマーカーで動くWebサイトを用意します。

ソースのサンプルはここをクリックすると表示
sampleAFrame-AR.html
<!DOCTYPE html>
<html class=""><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>sample</title>
    <body style="margin: 0px 0px 0px 126.5px; overflow: hidden;">
        <script src="https://aframe.io/releases/0.8.2/aframe.min.js"></script>
    <script src="https://cdn.rawgit.com/jeromeetienne/AR.js/1.5.5/aframe/build/aframe-ar.js"></script>
    <a-scene embedded="" arjs="trackingMethod:best; debugUIEnabled:false;" class="" canvas="" inspector="" keyboard-shortcuts="" screenshot="" vr-mode-ui="">
      <a-marker preset="hiro" position="" rotation="" scale="" visible="" material="" arjs-anchor="" arjs-hit-testing="">
        <a-box position="0 0.8 0" wireframe="false" color="#843233" rotation="" scale="" visible="" material="" geometry="" src="" width="0.7" height="0.7" depth="0.7" >
          <a-animation attribute="rotation" from="20 0 0" to="20 360 0" direction="alternate" dur="4000" repeat="indefinite" easing="ease"></a-animation>
        </a-box>
    </a-marker>
    <a-entity camera="" position="" rotation="" scale="" visible=""></a-entity>
    <canvas class="a-canvas" data-aframe-canvas="true"></canvas>
    <a-entity light="" data-aframe-default-light="" aframe-injected="" position="" rotation="" scale="" visible=""></a-entity>
    <a-entity light="" position="" data-aframe-default-light="" aframe-injected="" rotation="" scale="" visible=""></a-entity>
  </a-scene>
</body>
</html>


このように3Dオブジェクトが表示されたら成功です。

オリジナルマーカーを作ろう

ありがたいことにジェネレータサイトをがあります。こちらを使わせて頂きます。

UPLOADボタンでARマーカーにしたい画像をアップロードします。

DOWNLOAD MARKERDONWLOAD IMAGEボタンでARマーカーとARマーカーのパターンファイルをダウンロードします。

以下のようなファイルがダウンロードできます。

  • pattern-test.png
  • pattern-test.patt

sampleAFrame-AR.htmlの修正

sampleAFrame-AR.htmlの修正し、パターンファイルpattern-test.pattを設定します。
同じ階層(同じフォルダ)にpattern-test.pattを格納し、sampleAFrame-AR.htmlを以下のように修正します。

<!-- 変更前 -->
<a-marker preset="hiro" position="" rotation="" scale="" visible="" material="" arjs-anchor="" arjs-hit-testing="">
<!-- 変更後 -->
<a-marker type="pattern" url="pattern-test.patt">

これで、オリジナルのARマーカーの設定が完了しました。

動作確認

pattern-test.pngで3Dオブジェクトが表示されれば成功です。

※おまけ※
ARマーカーをいくつか作って検証した結果はこちら
どんなARマーカーが認識できるか試してみた

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

【初Qiita】プログラミング2ヶ月目でリリース、現在も使われているWebアプリ『SHISHA Log』

1. プログラミングを初めて4ヶ月が経過しました。

はじめまして、赤岸叶恵です(Twitter@KKKEAS)
初めてQiitaに投稿します。当記事への訪問ありがとうございます!

私は現在G's Academy TOKYOで未経験からプログラミングを学習しています。
ありがたいことに開発した『SHISHA Log』にユーザー投稿が50件集まり、形になってきたので備忘録としてこの記事を書いています。

2. 『SHISHA Log』 = シーシャの美味しいミックスをメモするだけのアプリ

動作環境:モバイルGoogle Chrome
SHISHA Log リンク:https://chan-kanae.sakura.ne.jp/shisha_/login.html

ezgif.com-resize.gif

① なぜ開発したのか

私は一時期週8でシーシャ屋に行っていたほどシーシャ(水煙草)が好きです。
シーシャは複数のフレーバーをミックスして吸うことができるのですが、3種類以上になると
「この前のミックス美味しかったな。またやりたいけど何のフレーバーが入ってたか忘れちゃった。再現できない:sob:
という問題が発生します。

そのため

  • 美味しいミックスを思い出すため
  • 他ユーザーの投稿をみて「このミックス美味しそう」→シーシャ屋訪店→シーシャ界が活性化するといいなと考え

シーシャの美味しいミックスをメモするだけのアプリ『SHISHA Log』を開発しました!

② 仕様した技術

  • HTML5
  • CSS3
  • JavaScript
  • PHP
  • Firebase

③ 実装した機能/工夫した点

  • ホーム画面=タイムライン。自分の投稿のみ<編集/削除>アイコンを表示
  • マイページ。「自分の投稿一覧はよ」というユーザーの要望に応える形で実装

3. 今後実装したい機能/使いたい技術

現時点ではフロントJavaScript、サーバー_PHPで動いていますが
今後はフロント
Vue.js or Nuxt.js、サーバー_Laravelに移行し、以下の機能を 実装していきます。
目指せ週1アップデート!

  • ユーザー情報(名前・アイコン等)登録
  • 投稿のブックマーク機能
  • あいまい検索
  • 投稿をTwitter連携でシェア機能

4. 自戒を込めて。1番ハマった、データベース接続の構文

どハマりしました。3時間溶かしました。

動かない構文

 PDO('mysql:dbname= mydb;charset=utf8;host=localhost','root','root');

動く構文

 PDO('mysql:dbname=mydb;charset=utf8;host=localhost','root','root');

mysql:dbname= mydb ←問題はこの半角スペースでした。
正しいデータベース名は「mydb」ですが「 mydb」というデータベース名で接続しにいってたので
「そんなデータベースないよ〜」とPHPは思っていたようです。

5. 今後どんなエンジニアを目指すのか

事業会社のWebエンジニアとしてサービスのグロースハックに取り組みたいと考えています!

フロントサイドかサーバーサイドかでいうと、まだ決められません。
つい最近まではずっとフロント志望だったのですが、「どのサービスも情報を送受信して、表示するだけ。データベースに強くなりたいな」などと考えはじめています。

ひとまずVue.js or Nuxt.js/Laravelで開発を進めながら、何エンジニアになるのか決めようと思います!

そしてインターン先を探しています。インターンさせてください!

______________________

最後まで読んでくださりありがとうございます!

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

【初Qiita】プログラミング2ヶ月目でリリース、3ヶ月使われ続けるWebアプリ『SHISHA Log』

1. プログラミングを初めて4ヶ月が経過しました。

はじめまして、赤岸叶恵です(Twitter@KKKEAS)
初めてQiitaに投稿します。当記事への訪問ありがとうございます!

私は現在G's Academy TOKYOで未経験からプログラミングを学習しています。
ありがたいことに開発した『SHISHA Log』にユーザー投稿が50件集まり、形になってきたので備忘録としてこの記事を書いています。

2. 『SHISHA Log』 = シーシャの美味しいミックスをメモするだけのアプリ

動作環境:モバイルGoogle Chrome
SHISHA Log リンク:https://chan-kanae.sakura.ne.jp/shisha_/login.html

ezgif.com-resize.gif

① なぜ開発したのか

私は一時期週8でシーシャ屋に行っていたほどシーシャ(水煙草)が好きです。
シーシャは複数のフレーバーをミックスして吸うことができるのですが、3種類以上になると
「この前のミックス美味しかったな。またやりたいけど何のフレーバーが入ってたか忘れちゃった。再現できない:sob:
という問題が発生します。

そのため

  • 美味しいミックスを思い出すため
  • 他ユーザーの投稿をみて「このミックス美味しそう」→シーシャ屋訪店→シーシャ界が活性化するといいなと考え

シーシャの美味しいミックスをメモするだけのアプリ『SHISHA Log』を開発しました!

② 仕様した技術

  • HTML5
  • CSS3
  • JavaScript
  • PHP
  • Firebase

③ 実装した機能/工夫した点

  • ホーム画面=タイムライン。自分の投稿のみ<編集/削除>アイコンを表示
  • マイページ。「自分の投稿一覧はよ」というユーザーの要望に応える形で実装

3. 今後実装したい機能/使いたい技術

現時点ではフロントJavaScript、サーバー_PHPで動いていますが
今後はフロント
Vue.js or Nuxt.js、サーバー_Laravelに移行し、以下の機能を 実装していきます。
目指せ週1アップデート!

  • ユーザー情報(名前・アイコン等)登録
  • 投稿のブックマーク機能
  • あいまい検索
  • 投稿をTwitter連携でシェア機能

4. 自戒を込めて。1番ハマった、データベース接続の構文

どハマりしました。3時間溶かしました。

動かない構文

 PDO('mysql:dbname= mydb;charset=utf8;host=localhost','root','root');

動く構文

 PDO('mysql:dbname=mydb;charset=utf8;host=localhost','root','root');

mysql:dbname= mydb ←問題はこの半角スペースでした。
正しいデータベース名は「mydb」ですが「 mydb」というデータベース名で接続しにいってたので
「そんなデータベースないよ〜」とPHPは思っていたようです。

5. 今後どんなエンジニアを目指すのか

事業会社のWebエンジニアとしてサービスのグロースハックに取り組みたいと考えています!

フロントサイドかサーバーサイドかでいうと、まだ決められません。
つい最近まではずっとフロント志望だったのですが、「どのサービスも情報を送受信して、表示するだけ。データベースに強くなりたいな」などと考えはじめています。

ひとまずVue.js or Nuxt.js/Laravelで開発を進めながら、何エンジニアになるのか決めようと思います!

そしてインターン先を探しています。インターンさせてください!

______________________

最後まで読んでくださりありがとうございます!

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

【初Qiita】プログラミング2ヶ月目でリリース、3ヶ月間使われ続けるWebアプリ『SHISHA Log』

1 プログラミングを初めて4ヶ月が経過しました。

はじめまして、赤岸叶恵です(Twitter@KKKEAS)
初めてQiitaに投稿します。当記事への訪問ありがとうございます!

私は現在G's Academy TOKYOで未経験からプログラミングを学習しています。
ありがたいことに開発した『SHISHA Log』にユーザー投稿が50件集まり、形になってきたので備忘録としてこの記事を書いています。

1.5 前提_シーシャ(水煙草)とは

シーシャ(水煙草)とは、イスラム圏を発祥とする喫煙具の一種です。

仕組み

シロップ漬けしたタバコの葉をハガルという専用の容器に入れ、その上に熱した炭を置く。
炭で熱された味のついた煙を、ボトルに入っている水を通してホースから口に入れるという吸い方をします。ボトルに入っている水は紙巻き煙草でいうところのフィルターにあたります。
1台のシーシャで2時間ほど楽しめます。
ezgif.com-resize (2).jpg

シーシャの魅力

①煙が美味しい!
②コミュニケーションツールとして優秀!
③シーシャ屋ってサードプレイスとして最高!
の3本柱です。これ。

①煙が美味しい!

フレーバーは何百種類もあります。たとえば一部ですが
フルーツ系:グレープフルーツ、ライチ、ブルーベリー、キウイ
ハーブ系:ミント、ジャスミン、ローズ
スパイス系:アールグレイ、カルダモン、パンラズナ(白檀)
これらのフレーバーを単品やミックスして吸うことができます。そう、楽しみ方は無限大!

②コミュニケーションツールとして優秀!

シーシャは1台で2時間ほど楽しめます。ゆっくり深呼吸で煙を吸って吐くので気分が落ち着きます。
お酒がテンション上がるアッパー系だとしたら、シーシャはチル、ダウナー系です。
心の緊張が解けて会話が捗ります。友人恋人等プライベートで過ごす時間に最適。仕事のミーティングにも最適。コミュニケーションツールとして優秀。

③シーシャ屋ってサードプレイスとして最高!

家でも職場でもない自分の居場所って、社会人になると減りがちですよね。
そこでシーシャ屋。
気に入ったシーシャ屋に通っているうちに店員さん、常連さんと自然と仲良くなります。程よい距離感で安心できる場所。夜に行けば「おかえり」などと声をかけてくれる。サードプレイスがあるっていいなあ。

まだシーシャを体験したことのない方、百聞は一呼吸に如かずということで一度シーシャに行ってみてください!
ezgif.com-resize (1).jpg

2 『SHISHA Log』 = シーシャの美味しいミックスをメモするだけのアプリ

動作環境:モバイルGoogle Chrome
SHISHA Log リンク:https://chan-kanae.sakura.ne.jp/shisha_/login.html

ezgif.com-resize.gif

① なぜ開発したのか

私は一時期週8でシーシャ屋に行っていたほどシーシャ(水煙草)が好きです。
シーシャは複数のフレーバーをミックスして吸うことができるのですが、それゆえ中に何が入っていたか忘れます。
<アンブロシア+ジャスミン+赤林檎+ココナッツ+パーティーアニマル>とか100%忘れます。
その結果「この前吸ったミックス美味しかったな。またやりたいけど何のフレーバーが入ってたか忘れちゃった。再現できない:sob:
という問題が発生します。

そのため

  • 美味しいミックスを思い出すため
  • 他ユーザーの投稿をみて「このミックス美味しそう」→シーシャ屋訪店→シーシャ界が活性化するといいなと考え

シーシャの美味しいミックスをメモするだけのアプリ『SHISHA Log』を開発しました!

② 仕様した技術

  • HTML5
  • CSS3
  • JavaScript
  • PHP
  • Firebase

③ 実装した機能/工夫した点

  • ホーム画面=タイムライン。自分の投稿のみ<編集/削除>アイコンを表示
  • マイページ。「自分の投稿一覧はよ」というユーザーの要望に応える形で実装

3 今後実装したい機能/使いたい技術

現時点ではフロントJavaScript、サーバー_PHPで動いていますが
今後はフロント
Vue.js or Nuxt.js、サーバー_Laravelに移行し、以下の機能を 実装していきます。
目指せ週1アップデート!

  • ユーザー情報(名前・アイコン等)登録
  • 投稿のブックマーク機能
  • あいまい検索
  • 投稿をTwitter連携でシェア機能

4 自戒を込めて。1番ハマった、データベース接続の構文

どハマりしました。3時間溶かしました。

動かない構文

 PDO('mysql:dbname= mydb;charset=utf8;host=localhost','root','root');

動く構文

 PDO('mysql:dbname=mydb;charset=utf8;host=localhost','root','root');

mysql:dbname= mydb ←問題はこの半角スペースでした。
正しいデータベース名は「mydb」ですが「 mydb」というデータベース名で接続しにいってたので
「そんなデータベースないよ〜」とPHPは思っていたようです。

5 今後どんなエンジニアを目指すのか

事業会社のWebエンジニアとしてサービスのグロースハックに取り組みたいと考えています!

フロントサイドかサーバーサイドかでいうと、まだ決められません。
つい最近まではずっとフロント志望だったのですが、「どのサービスも情報を送受信して、表示するだけ。データベースに強くなりたいな」などと考えはじめています。

ひとまずVue.js or Nuxt.js/Laravelで開発を進めながら、何エンジニアになるのか決めようと思います!

そしてインターン先を探しています。インターンさせてください!

______________________

最後まで読んでくださりありがとうございます!

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