20200226のJavaScriptに関する記事は30件です。

【DX】Ryzen9 3950xは開発体験をどれほど向上させるか

Ryzen9 3950xは開発体験をどれほど向上させるか

はじめに

AMD Ryzenの登場の登場により、それまでIntel寡占状態だったCPU業界の動きがかなり激しくなっています。僕も先日「AMD Ryzen9 3950x」搭載のデスクトップPCを購入しました。
「AMD Ryzen 9 3950X」はメインストリーム向けCPUとして、世界初の16コア32スレッドCPUです。そんな驚異的な性能でどのくらい開発体験が向上するかを検証していきたいと思います。

比較環境

パッと思いつく検証方法がなかったのでReactのbuild時間を計測することにしました。
自作PCにはmacOSを搭載することができないのでUbuntuOSを利用しています。

検証PC

PC CPU OS Memory
iMac 2019 Intel(R) Core(TM) i5-8500 CPU @ 3.00GHz macOS 10.15.3 40GB
MacBook Pro 2017 Intel(R) Core(TM) i7-7660U CPU @ 2.50GHz macOS 10.15.3 16GB
Ryzen9 3950X PC AMD Ryzen™ 9 3950X Ubuntu 18.04 32GB

ビルド対象

項目
言語 JavaScript
フレームワーク React
bundler webpack
Node Version 10.16.3
ファイル数 1404
行数 133363

結果

Development Build

まずはDevelopment Buildで検証します。
以下のコマンドを実行したときにブラウザでロードされるまでの時間をキャッシュ有(1回目)とキャッシュ無(2回目)計測します。

$ yarn start
  • キャッシュ無
PC Time
Macbook Pro 4:01.04
iMac 1:01.53
3950x PC 0:42.63
  • キャッシュ有
PC Time
Macbook Pro 1:44.14
iMac 0:41.78
3950x PC 0:30.82

Production Build

まずはProduction Buildで検証します。
以下のコマンドを実行したときにbundleファイルが生成され終わるまでの時間を計測します。

$ yarn build
PC 1回目
Macbook Pro 4:40.63
iMac 2:32.74
3950x PC 1:42.44

まとめ

じつはこの3台、購入時期は差があるものの値段的にはあまり差がありません。
OSは違えどRyzenのコストパフォーマンの高さがうかがえますね。

そもそもデスクトップPCとノートパソコンではCPUの設計が異なります。型番の最後に"U"がつくとそれはモバイル用に設計された省電力CPUなのであまりパフォーマンスがでません。そのぶんMacBookProは持ち運ぶことができますが。

1日に2回キャッシュ無、10回キャッシュ有ビルドをした場合、
最速のRyzenとMacBookでは一日あたり20分、一年で120時間の差にになります。
それ以外の差も含めるともっと差がでることになります。

ゲーム業界やデザイナー業界だけでなく、デベロップ環境でもAMDの勢いを感じることができました。
AMD最高です。

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

【DX】Ryzen9 3950Xは開発体験をどれほど向上させるか

Ryzen9 3950Xは開発体験をどれほど向上させるか

はじめに

AMD Ryzenの登場により、それまでIntel寡占状態だったCPU業界の動きがかなり激しくなっています。僕も先日「AMD Ryzen9 3950X」搭載のデスクトップPCを購入しました。
「AMD Ryzen9 3950X」はメインストリーム向けCPUとして、世界初の16コア32スレッドCPUです。そんな驚異的な性能でどのくらい開発体験が向上するかを検証していきたいと思います。

比較環境

パッと思いつく検証方法がなかったのでReactのbuild時間を計測することにしました。
自作PCにはmacOSを搭載することができないのでUbuntuOSを利用しています。

検証PC

PC CPU OS Memory
iMac 2019 Intel(R) Core(TM) i5-8500 CPU @ 3.00GHz macOS 10.15.3 40GB
MacBook Pro 2017 Intel(R) Core(TM) i7-7660U CPU @ 2.50GHz macOS 10.15.3 16GB
Ryzen9 3950X PC AMD Ryzen™ 9 3950X Ubuntu 18.04 32GB

ビルド対象

項目
言語 JavaScript
フレームワーク React
bundler webpack
Node Version 10.16.3
ファイル数 1404
行数 133363

結果

Development Build

まずはDevelopment Buildで検証します。
以下のコマンドを実行したときにブラウザでロードされるまでの時間をキャッシュ有(1回目)とキャッシュ無(2回目)計測します。

$ yarn start
  • キャッシュ無
PC Time
Macbook Pro 4:01.04
iMac 1:01.53
3950x PC 0:42.63
  • キャッシュ有
PC Time
Macbook Pro 1:44.14
iMac 0:41.78
3950x PC 0:30.82

Production Build

つぎにProduction Buildで検証します。
以下のコマンドを実行したときにbundleファイルが生成され終わるまでの時間を計測します。

$ yarn build
PC 1回目
Macbook Pro 4:40.63
iMac 2:32.74
3950x PC 1:42.44

まとめ

じつはこの3台、購入時期は差があるものの値段的にはあまり差がありません。
OSは違えどRyzenのコストパフォーマンの高さがうかがえますね。

そもそもデスクトップPCとノートパソコンではCPUの設計が異なります。型番の最後に"U"がつくとそれはモバイル用に設計された省電力CPUなのであまりパフォーマンスがでません。そのぶんMacBookProは持ち運ぶことができますが。

1日に2回キャッシュ無、10回キャッシュ有ビルドをした場合、
最速のRyzenとMacBookでは一日あたり20分、一年で120時間の差にになります。
それ以外の差も含めるともっと差がでることになります。

ゲーム業界やデザイン業界だけでなく、デベロップ環境でもAMDの勢いを感じることができました。
AMD最高です。

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

Top 50+ React Native Interview Questions and Answers 2020

Have a look at these mostly asked best 50+ React Native Interview Questions and their answers in 2020. Get ready to face any question on React Native thrown at you in the interview.

click here to read more
https://www.positronx.io/top-react-native-interview-questions-and-answers/

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

Top 40+ Spring Boot Interview Questions & Answers 2020

most asked Spring Boot Interview Questions with Answers list of 2020.

click here to read more
https://www.positronx.io/top-spring-boot-interview-questions-answers/

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

Top 30+ CSS & CSS3 Interview Questions and Answers 2020

Mostly asked CSS & CSS3 Interview Questions with Answers in 2020. Reading these questions once will give you a ton of confidence, and you will be ready to move out for an interview. Let’s begin.

click here to read more
https://www.positronx.io/top-css-css3-interview-questions-and-answers/

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

React Native Firebase - Firebase Integration in React Native

React Native is a well known open-source framework created by Facebook. It allows you to build a native mobile application with the help of JavaScript.

clic here to read more
https://www.positronx.io/react-native-firebase-firebase-integration-in-react-native/

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

Top 30 Python Interview Questions with Answers 2020

Here are the top 30 python interview questions and answers in 2020 you should read before appearing in the interview.

click here to read more
https://www.positronx.io/python-interview-questions-with-answers/

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

Create Radio Button Component in React Native

This tutorial shows you how you can create a radio button component in React Native application pretty smoothly. We will learn the easiest way to deal with React Native Radio Buttons.

Read more to click here
https://www.positronx.io/create-radio-button-component-in-react-native/

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

three.js でタッチ系のイベントが発火しない時

環境

three.js - 113

困ったこと

clickに設定したイベントがマウスクリックでは問題ないのに、タッチで発火しないという事象に悩まされる。

こんな感じでイベントを設定↓

window.addEventListener('click', () => {
  // 発火せず
});

ちなみに、タッチ系もだめだった↓

window.addEventListener('touchstart', Callback);// 発火せず

原因

three.jsにサンプルとして付属している OrbitControls を使っていることが原因。
内部で各種イベント伝播が 「食べられて」しまう実装になっている。

「食べられて」が正しいのか知りませんが、こちらのIssueで気に入ったので使っていきます。
https://github.com/mrdoob/three.js/issues/16254

対策

OrbitControls.js から event.preventDefault() を削除することで、イベントを食べられないようにすることができます。

https://github.com/mrdoob/three.js/blob/dev/examples/js/controls/OrbitControls.js

OrbitControls.js
  function onTouchStart( event ) {
    if ( scope.enabled === false ) return;
      // ↓こいつが邪魔なのでコメントアウト
      // event.preventDefault(); // prevent scrolling
      switch ( event.touches.length ) {

これで、clickに設定したイベント処理が実行されタッチパネルでも問題なく動くようになりました。

ちなみに、この変更による副作用も存在するようなのでご自身の環境で問題ないことを確認してくださいね。
(私の環境では特に問題なかった。多分)
https://github.com/mrdoob/three.js/pull/18612

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

[ES5]多くのWebエンジニアができていないクライアントサイドの書き方(JavaScriptのアンチパターン等) ES5

フリーのエンジニアです。
出向先でクライアントサイドについて話す事がありましたので、記事にしました。
ES5に関する記事なので、古いブラウザ(ie11など)に対応することを想定してください。
メモした内容をそのまま貼り付けてます。わかりにくいかも。

厳密等値演算子を使おう

 ==,!= はアンチパターン

Promiseを使おう

 ES5でもpolyfillを利用すれば、Promiseは使える
 積極的に使う事

クラスの書き方

 コンストラクタはこう書く var MyClass = function() { ... };
 メソッドはこう書く #MyClass.prototype.methodA = function() { ... };

組み込みオブジェクト(Mathのようなオブジェクトを作りたい時)の書き方

 モジュールパターンを使うとよい

varをいくつも書かない

カンマで区切ろう

varのスコープは関数スコープ

 ブロックスコープはES5には無いよ。
 なるべく、関数の先頭にvarを書く

配列の初期化は[]

 new Array()と書いてはいけない。オブジェクトの初期化は{}。new Object() と書いてはいけない。

配列のループ処理は、foreach,reduce等を使う 

 for文は仕方なく使う感じ

call,applyを使いこなすと便利 thisを固定できる

 thisは動的に決まる
 thisを正しく理解する事

1モジュール1ファイルという決まりは無い

 クラスのメソッドがたくさんある場合、機能別に別ファイルにしても構わない

プログラムのエントリポイントは意識せよ

 エントリポイントは1箇所にすべき
 jQueryを使っているなら、
 $(function() { });
これの呼び出しは1箇所にして、ここで全部初期化しましょう。

グローバル変数はなるべく少なくしよう

 サーバーが生成する変数は1つのオブジェクトで受け取ろう

クライアントサイドMVCを意識してプログラミングしよう。

 見通しが良くなります
 悪い例 ビューに変数を埋め込む事

同じ処理を2回以上書かない

 当たり前の事ですが、できていない人が多すぎる。関数にしましょう。

$(selector)の戻り値は配列ではない

 配列ではありません。オブジェクトのような配列です。(object-like array)

ES5でもデフォルト引数のようなことできる

 function test(a) { a = a || 10 } // 引数が正の整数を期待している場合のサンプル

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

JavaScriptのメモ

これは何?

個人的なメモです。

置換

repelace (非破壊)

let some_word = '123456789'
some_word.replace(/4567/, '')
> "12389"

console.log('some_word=[%s]', some_word)
> some_word=[123456789]

Array

破壊を非破壊処理にする

let some_array = [1, 2, 3, 4, 5, 6, 7, 8]

some_array.pop() // 破壊
some_array.slice().pop() // 非破壊

サイズ指定&同じ数値で初期化

let N = 10
let init_num = 0
let some_array = new Array(N).fill(0)

Math.max()

let some_array = [1, 2, 3, 4, 5, 6, 7, 8]
let max_num = Math.max(...some_array)

数値としてsort() [破壊]

let some_array = [9, 1, 12, 13, 4, 15, 6, 7, 8]
// 破壊
some_array.sort( (a, b) => { return a - b }) // 昇順
some_array.sort( (a, b) => { return b - a }) // 降順

forEachでkey,value

let some_array = [9, 1, 12, 13, 4, 15, 6, 7, 8]
some_array.forEach( (val, key) => {
  console.log('key=[%s] val=[%i]', key, val)
})

要素分割

let some_array = [9, 1, 12, 13, 4, 15, 6, 7, 8]

some_array.slice().splice(3, 5) // index=3 から 5 要素 
> (3) [13, 7, 8]

some_array.slice().slice(5) // index=6 から最後までの要素
> (4) [15, 6, 7, 8]

Object

ObjectのキーをArrayで取得

let some_obj = {
  a: 0,
  b: 1,
  c: 2
}
let keys = Object.keys(some_obj)

ObectをforEach

let some_obj = {
  a: 0,
  b: 1,
  c: 2
}
Object.keys(some_obj).forEach( key => {
  let val = some_obj[key]
  console.log('key=[%s] val=[%i]', key, val)
})

console

%s : String
%d or %i : Number
%f : Floating points
%o : an Object
%j : JSON

Reference

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

JavaScriptでフォーカスイン/アウトを自動的に行う

はじめに

WEB画面の入力項目には、フォーカスアウト時のイベントを定義することができます。
自動的にフォーカスイン/アウトを行えば、画面の初期表示時に動作させることも可能です。

やったこと

JavaScriptでフォーカスイン/アウトを自動的に行う実装をしました。

document.form.item01.focus();
document.form.item01.blur();

わかったこと

フォーカス関連のイベントを呼び出す場合は、formタグのname属性、inputタグのname属性を使用する。

つぎにやること

入力項目のフォーカスアウト時のイベントをピタゴラスイッチ的に呼び出す。

メリット

入力項目のフォーカスアウト時のイベントを流用できる。

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

JavaScriptでjpGridの列をカンマ区切りで結合する

はじめに

JavaScriptには、jpGridというグリッドテーブルを扱うライブラリがあります。
グリッドテーブル内の値をカンマ区切りで結合し、任意の項目に挿入しました。

やったこと

JavaScriptでjpGridの列をカンマ区切りで結合しました。

var gt = $("#gt").getRowData();
var result = '';
for(var i=0; i<gt.length; i++){
    result += gt[i].item01;
    if(i < gt.length - 1) result +=', ';
}
document.getElementsByName('item02')[0].value = result;

わかったこと

jpGridのgetRowData()の結果は配列で返ってくるため、配列の長さを取得したりfor文で繰り返しをすることができる。

つぎにやること

カンマ区切りで結合した項目を一覧画面などに表示する

メリット

一覧画面でグリッドテーブル内の値を検索することができる

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

静的なWEBサイト用にも使えるstorybookのpug環境を作ってみた

storybookって色々使い道があると思うのですが、動的なWEBサイト用の情報しか殆どないので静的WEBサイトでも使えるようなものを作ってみました。
やりたい事が形になったので紹介したいと思います。
gitも公開しているので良かったお試しください。

story01.jpg

DEMO
https://github.com/BigFly3/storybook_html_pugdev

出来ること

・/stories以下のtree-title.stories.pugを/path/to/tree-titleへ自動追加(日本語も可)
・/stories以下のsassやstylusを自動で読み込み
・パネルにpugとhtmlのソース表示&コピー
・pugのmixinでコンポーネント作成し、knobsやテストをjsで記述可
・複数の環境に合わせて設定を変更して構築可

サブの環境として使えます

(.stories.)pug + sass,stylus + 一応jqueryを使えるようにしてあるので、storybookやjsの知識があまり無い状態でも、マークアップからstorybook上での開発が進められるようにしてあります。
プロジェクトの本環境を作成している間に、コンポーネントを先に開発してもらったりや既存のgulp環境などの横に並べて、stories以下のファイルを本環境でincludeして使用するみたいなことも可能になると思います。

pugとhtmlのソース表示&コピー

アドオンのパネルにpugとhtmlのソースを表示することができます。
※自作のpugパネルはホットリロードが効きません。変更確認したい場合はリロードしてください。

pugパネルのソース追加方法

【.stories.pug】で追加したストーリー → 自動的にパネルに追加されます
【.stories.js】で追加したストーリー → parametersの【pugCode】に/stories/以下のパスを入れる

//csf
export const Default = () => {/省略/}
Default.story = {
  parameters:{pugCode:'Examples/Media/Media.pug'}
}

//storiesOf
storiesOf('Examples/Media',modules)
.add('Media', () => {/省略/},
{pugCode:'Examples/Media/Media.pug'})

pugのmixinで作るコンポーネント

静的なHTMLのためのコンポーネント駆動開発と言う記事がとても参考になるのですが、pugのmixinを使用することでstorybookの形に合うコンポーネント作成をすることが出来ます。
※こちらの記事のテスト以外の動作サンプルも一緒に入れてあります。

リンクボタンの作成サンプル

大中小サイズ、横幅フルサイズ、色、URL、外部リンクの設定ができるコンポーネントをpugのmixinで作成します。

/stories/Components/LinkButton/LinkButton.pug
mixin LinkButton(params={})
  - const props = Object.assign({ url: '' ,full:false}, params) //デフォルトのパラメータ

  - let className = attributes.class !== undefined ? attributes.class : ''
  - if(props.size) className += ' LinkButton--' + props.size
  - if(props.full) className += ' LinkButton--full'
  - if(props.color) className += ' LinkButton--' + props.color
  - if(props.isBlank) Object.assign( attributes , {'target':'_blank'})
  - const attrs = Object.assign( attributes , {class:className})
  a.LinkButton(href=props.url)&attributes(attrs)
    block
/stories/Components/LinkButton/LinkButton.styl
.LinkButton
  padding 10px 50px
  text-decoration none
  color #000000
  position relative
  text-align center
  border 1px solid #CCCCCC
  border-radius 5px
  font-size 16px
  display inline-flex
  justify-content center
  align-items center
  box-sizing border-box

  /* サイズ */
  &--small
    padding 5px 50px
    font-size 12px

  &--large
    font-size 24px

  /* フルサイズ */
  &--full
    width 100%

  /* カラー */
  &--gray
    background #8c9094
    border 1px solid #8c9094
    color #ffffff

  &--green
    background green
    border 1px solid #FFF
    color #ffffff

こんな感じmixinを作ると【stories.pug】では、コンポーネントベースのコードで書くことが出来ます。
knobsまで作らなくても色々なパターンを作成しておけば、コピー用サンプルや見た目のテスト的な使用なども可能です。

/stories/Components/LinkButton/サンプル.stories.pug
include LinkButton
p
  +LinkButton({
  })
    | ノーマル
p
  +LinkButton({
    size:'small',
    color:'green',
  })
    | 緑小
p
  +LinkButton({
    size:'large',
    color:'gray',
    url:'https://google.com',
    isBlank:true
  })
    | グレー大
p
  +LinkButton({
    size:'small',
    full:true,
  })(class="addClass")
    | 小フルサイズ

mixin_sample.jpg

knobsを書くJSのサンプル

今のコンポーネントをKnobs化するサンプルです。

/stories/Components/LinkButton/LinkButton.stories.js
import { withKnobs, boolean, select ,text} from '@storybook/addon-knobs'
import { renderer } from 'storypug'
import LinkButton from './LinkButton.pug'
const { html } = renderer()

const sizes = {
  'ノーマルボタン':'',
  '小さいボタン':'small',
  '大きいボタン':'large',
}
const colors = {
  'デフォルト':'',
  'グレー':'gray',
  'グリーン':'green',
}

export default {
  title: 'Components/LinkButton',
  decorators: [withKnobs],
}

// knobs
export const Knobs = () =>
html(
  LinkButton,
  {
    size: select('ボタンタイプ', sizes, sizes[0]),
    full: boolean('フルサイズ', false),
    color: select('', colors, colors[0]),
    url: text('URL',''),
    isBlank: boolean('別窓', false),
  },
  'ボタンテキスト',
)
Knobs.story = {
  parameters:{pugCode:'Components/LinkButton/LinkButton.pug'}
}

knobs_sample.jpg

ビルドについて

githubpagesとテスト環境にそれぞれ作成したいというようなケースのために、ビルド設定を二つ用意しています。

  • .storybook/default → 【npm run build】 元の設定と同様publicフォルダを設定しています。
  • .storybook/second → 【npm run build2】 githubpagesを想定してdocsフォルダを設定しています。

.storybook/defaultをコピーしてpackage.jsonのscriptsを修正すれば、さらに複数作ることも可能です。
staticフォルダも元の設定と同様staticなのでメイン環境などと合わせる場合は、この辺も調整してください。

ビルドにおけるpugの共通変数の設定

ファイルを置くパスが違うケースもあると思うので、それぞれの環境にpugの共通変数用としてpug-data.jsに変数をセットする仕組みを用意しています。
通常layout.pugなどに設定するような変数をここにセットすることで、コンポーネントを修正せずに出し分けすることができます。
【stories.pug】による自動追加でも、この共通変数が一緒にセットされます。

pug-dataの使い方

先ほど作成したLinkButtonを使用して、外部リンク設定時にアイコンのimgタグを表示するように変更してみます。

1 画像フォルダの共通変数を設定

/.storybook/default/pug-data.js
//pug common values
module.exports = {
  imgDir:"/images/"
}

2 LinkButton.pugコンポーネントの【block】の下にblankのif文を追記

/stories/Components/LinkButton/LinkButton.pug
  a.LinkButton(href=props.url)&attributes(attrs)
    block
    -if(props.isBlank)
      img(src=imgDir + 'icon-blank.png' width="16px" style="margin-left:10px")

3 LinkButton.stories.jsの【renderer()】の行をpug-data.jsを読み込むように修正

/stories/Components/LinkButton/LinkButton.stories.js
import pugData from '@conf/pug-data'
const { html } = renderer(pugData)

別窓チェック時に共通変数で設定する画像アイコンが表示できるようになります。
blank画像.jpg

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

javascriptでenvファイルを使う

javascriptでenvファイルを使う実装をまとめます。

前提条件

  • npmがインストールされていること

インストール

npmコマンドでモジュールをインストール

$ npm i --save dotenv

使い方

ファイルの作成

touchコマンドでtest.jsファイル、envファイルを作成

$ touch test.js .env

test.js

require('dotenv').config()
const { BASE_URL } = process.env

console.log(BASE_URL)

.env

BASE_URL="http://localhost:8000/"

実行

nodeコマンドでtest.jsを実行する

node test

出力結果

http://localhost:8000/

Nuxt.jsで使う場合

npmコマンドでモジュールをインストール

$ npm i --save @nuxtjs/dotenv

nuxt.config.js

module.exports = {
  modules: [
    '@nuxtjs/dotenv', // 追記
  ]
}

参考文献

この記事は以下の情報を参考にして執筆しました。

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

GETとPOST

GET

普通にwebサイトにアクセスするとき、Webブラウザは「GET」という方式でアクセスをしています。
GETはアクセスの基本と考えていいでしょう。

GETは「いつ、どこからどうアクセスしても同じ結果が返されされる」というようなものに使います。

POST

「POST」といのは、フォームなどを送信するときの基本となる送信方式です。

POSTは「その時、その状況でその表示」を行うような場合に使われます。

参考

node.js超入門 p130

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

無限スクロールを実現する自作JQueryライブラリ loop.jsのご案内

はじめに

WebサイトのUXとして無限スクロールは既に一般的なUXとして採用されてきています。そこで制作してるサイトでもライブラリを用いて採用すると、思いのほか使い勝手が悪いことが判明し、ロジックを自作したものをライブラリとして公開するものです。このライブラリは下記の特徴(定義要件)の下で開発されたものです。

  1. 再読み込み時にもこれまでに読み込んだページがリセットされない
  2. 採用されたサイトにはフッターがあるためボタン操作で次のページが表示させる
  3. 100行前後の軽量なライブラリである

デモ・入手

このソースコードはGitHubから入手できます
https://github.com/emesh0620/jquery_loop_js

なお、デモは下記のページからご覧頂けます。
https://emesh0620.github.io/jquery_loop_js/

実装

html部

まずループさせたい箇所に次のコードを入力します。
なお、next_loop_btm_bodyとnext_loopを変更する際にはソースコードを変更する必要があります。

<div id = "next_loop"></div>
<span id = "next_loop_btm_body">
    <button id="next_loop_btm">もっと見る</button>
</span>

JS部

JavaScriptはたとえば次のコードになります。

<script type="text/javascript">
$(function() {
    //loop.jsの本体を呼び出す
    $('#next_loop_btm').loop_looping({
        name    : 'loop',
        url     : 'loop/$.html',
        allpage : 2,
    });

    //キャッシュをクリアしたい時に呼び出す
    $('#cache_clear').loop_looping_clear();
});
</script>
  • name・・・HTMLのキャッシュ時に使う名前
  • url・・・2ページ目以降のページを指示する注意点は下記の通りです。
    • かならず表示順の連番として、HTMLであれば全て同一のディレクトリ入れること。
    • 連番部分はJSに書き込む際には$で置換しておくこと。 (例えば loop/1.html loop/2.html...と続くなら loop/$.htmlとする)
    • なお、HTMLを吐き出すことが出来れば動的サイトなどでも構わない。
  • allpage・・・ループの総ページ数をかく。最初のページを入れた値を入れること。

キャッシュをクリアしたいときには適当な場所に、

$(任意のIDまたはClass).loop_looping_clear();

を挿入すればキャッシュはクリアされます。

おわりに

このライブラリはMITライセンスにつき、ご利用の環境によってご自由に改変して頂いて結構です。例えば、キャッシュにはUNIXTimeを記録していますので一定の時間が経てばキャッシュをクリアするなどの使い方があります。

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

Preactの始め方&Reactとの違い

Reactのサブセットライブラリとして、Preactというものがあります。より軽量なReactという位置付けで、ダウンロードするjsファイルのサイズが肥大しがちなSPAサイトにおいて、パフォーマンスを向上させるための有効な選択肢の一つとして捉えられているようです。

使い方などを勉強してみたので、メモ代わりとしてPreactプロジェクトの始め方、Reactとの違いなどについてまとめておきます。

Reactとのサイズ差

PreactはReactより軽量ということですが、具体的にどのくらい違うのでしょうか?公式ドキュメントには3kB!!と書かれています。それではReactのライブラリサイズは...?ということで、「BUNDLE PHOBIA」というサイトで調べてみると、

  • React: 6.4kB(minified), 2.6kB(minified & gzipped)
  • Preact: 9.5kB(minified), 3.7kB(minified & gzipped)

あら...?Reactのほうが軽くない...?と一瞬思ってしまうのですが、ReactをWebで使うには、react-domも必要です。こいつのサイズを見てみると、

  • react-dom: 114.6kB(minified), 36.2kB(minified & gzipped)

デカい!!Preactではreact-domは必要ないので、その分軽い、ということでしょうか。

Preactの始め方

Preactの公式ドキュメントに行くと、「Getting Started」というページがあり、その通りに進めればcreate-react-appに近い感じで簡単にプロジェクトをセットアップできます。

ただ、今回はその方法を使わずに手作業で必要なセットアップを行なっていきます。シンプルにするため、TypeScriptは導入しません。
まずは適当なディレクトリを作成し、npm initを入力。

mkdir preact-sample
npm init -y

なにはともあれ、Preactをインストールします。

npm i --save preact

js, jsxファイルをバンドルするため、Webpack系のライブラリをインストール。

npm i --save-dev webpack webpack-cli webpack-dev-server

続けて、jsxファイルをトランスパイルするためのライブラリをインストール。

npm i --save-dev @babel/core @babel/preset-env babel-core babel-loader babel-preset-preact

.babelrcを作成します。

.babelrc
{
  "presets": [
    "@babel/preset-env",
    "preact"
  ]
}

次に、webpack.config.jsを作成して設定を記述していきます。エントリポイントはsrc/index.jsx、バンドルファイルの出力先はdist/js/bundle.jsとします。

webpack.config.js
const path = require("path");
const webpack = require("webpack");

module.exports = env => {
  return {
    entry: "./src/index.jsx",
    output: {
      filename: "./js/bundle.js"
    },

    resolve: {
      extensions: [".js", ".jsx"]
    },

    module: {
      rules: [
        {
          test: /\.(js|jsx)$/,
          exclude: /node_modules/,
          use: ["babel-loader"]
        }
      ]
    },

    devServer: {
      contentBase: path.join(__dirname, "./dist"),
      watchContentBase: true,
    }
  }
}

次にdistディレクトリを作って、その直下にindex.htmlを作成。

index.html
<html>
<head>
  <title>Preact sample</title>
</head>
<body>
  <div id="app"></div>
  <script src="./js/bundle.js"></script>
</body>
</html>

最後にsrc/index.jsxに表示したいコンテンツを書けばOKです。

index.jsx
import { h, render } from "preact";

const App = () => {
  return <div>Hello World!</div>
};

render(<App />, document.getElementById("app"));

これでwebpackのdevServerを立ち上げれば、Hello World!が表示されるはずです。あとは、普段Reactを書いているのとほぼ同じ感覚で進めていけます。関数コンポーネントやhooksも使えます。

ReactとPreactの違い

Reactとは何が違うのか、どういった機能が使えないのかが気になるところですが、それらは公式ドキュメントにまとめられています。(Differences to React

そこまで分量も多くないので、自身で確認してみるのが一番良いと思います。Reactにあって、Preactにない機能の部分だけを抜粋します。

PropTypesによるバリデーションチェック

こういうのですね(以下のコードはReact公式ドキュメントからの抜粋です)。

import PropTypes from 'prop-types';

class Greeting extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

Greeting.propTypes = {
  name: PropTypes.string
};

型チェックをするなら今ならTypeScriptを使うことが多いはずなので、なくてもとくに困らないような気はします。ちなみにPropTypesが必要なら、preact-compatというライブラリを入れることで補完できるようです。

React.Children

React.Childrenについての解説はこちら

props.childrenはよく使いますが、React.Childrenは使ったことがないですね。。。こちらもpreact-compatで補完できるとのことです。

Synthetic Events

Reactではイベントハンドラに渡されるイベントは全てラップされたものになっています。ラップされたイベントインスタンスは同じAPIインターフェースを持つので、ブラウザ間の差異を意識することなく、イベント処理を行えます。また、Synthetic Eventsインスタンスはプールされ、同じインスタンスが使い回されます。そのために、イベント処理が終了するとインスタンスの各フィールドが全てnullで初期化されるという特徴も持っています。

この仕様を知らずにハマった記憶もありますが、PreactではSynthetic Events機能は提供せず、ブラウザネイティブのイベントインスタンスがイベントハンドラに渡されるようです。これは知っておくべき違いな気がしますね。

まとめ

パッと調べた感じだと、Preactでよくない?と思えるのですが、他ライブラリとの互換性などの問題もあるのかもしれません。まずは小さいプロジェクトで試してみて、実際に使った場合にどんな問題が起こるのかを把握してから適切に使いたいですね。

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

npmコマンドの直列処理、並列処理の簡単な記述

npmコマンドを簡単に記述して、直列処理、並列処理を行います。

前提条件

  • npmがインストールされていること
  • package.jsonがあること

インストール

$ npm install -D npm-run-all

使い方

設定

package.json
run-sの後にコマンドを書くと直列、run-pの後にコマンドを書くと並列で処理を行ってくれます。
コマンドをhello:*と書くと、当てはまるすべてのコマンドを処理します。

{
  "scripts": {
    "hello:foo": "echo FOO",
    "hello:bar": "echo BAR",
    "hello-s": "run-s hello:foo hello:bar",
    "hello-p": "run-p hello:*"
  }
}

実行

npmコマンドでhello-sまたはhello-pを実行します。

$ npm run hello-s
$ npm run hello-p

出力結果

FOO
BAR

参考文献

この記事は以下の情報を参考にして執筆しました。

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

JavaScriptで音声読み上げテスト

JavaScriptで音声読み上げのテストPGMを作成しました(空白を読み上げたい場合、'。'を記入)

参考にしたサイト
再生、一時停止、再開ボタン - Speech Synthesis API

githubURL: https://github.com/NanjoMiyako/TextYomiageTest

サンプルページURL: https://nanjomiyako.github.io/TextYomiageTest/

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

PlayCanvas v1.25.0アップデート内容

スクリーンショット 2020-02-26 12.49.13.png
PlayCanvasのアップデートについて
昨日PlayCanvas Engineがバージョン 1.24.7から 1.25.0にアップデートされました。

このアップデートでは、WebXRのサポートの追加、examplesの追加、ドキュメントの修正、バッチングに対してのアップデートが入りました。

機能追加

WebXRサポート

こちらのコミットでWebXRのソースコードが追加されました

Particle System コンポーネントについて

こちらのコミットでParticle texture atlas start frame propertyデモとソースコードが追加されました

この変更によりParticle Systemコンポーネントに対してanimStartFrameのプロパティを追加できるようになりました。

使ってみる

作成したプロジェクトはこちらです
https://playcanvas.com/project/667962/
このテクスチャをパーティクルとして使用する場合する際にanimStartFrameを設定します。

1. Particle Systemコンポーネントをを持つエンティティを作成する

a. Particle Systemを追加
スクリーンショット 2020-02-26 12.19.02.png

b. Color Map, Normal Mapにテクスチャを追加

この画像を使用します

particles-numbers.png

属性名
Color Map particles-numbers.png
Normal Map particles-numbers.png

スクリーンショット 2020-02-26 12.19.28.png

c. 各種プロパティを設定
今回適用するテクスチャアトラスは4x4のものなのでそれに沿った設定をしていきます。

属性名
Horizontal Tiles 4
Vertical Tiles Tiles 4
2. animStartFrameを設定するParticleSystemコンポーネントを持つエンティティを4つ複製

スクリーンショット 2020-02-26 12.11.36.png

3. animStartFrameを設定するスクリプトを追加

PlayCanvasのattributesを指定する方法でエディタからanimStartFrameを設定できるようにします。

/*jshint esversion: 6, asi: true, laxbreak: true*/
const Particle = pc.createScript('particle');
Particle.attributes.add("animStartFrame", {"type": "number"})

Particle.prototype.initialize = function() {
    this.entity.particlesystem.animStartFrame = this.animStartFrame
};

3.それぞれ値を設定する

それぞれのエンティティに対して、animStartFrameを設定します。

エンテティ1
属性名
animStartFrame 0

スクリーンショット 2020-02-26 12.12.53.png

エンテティ2
属性名
animStartFrame 4

スクリーンショット 2020-02-26 12.12.58.png

エンテティ3
属性名
animStartFrame 8

スクリーンショット 2020-02-26 12.13.02.png

エンテティ4
属性名
animStartFrame 12

スクリーンショット 2020-02-26 12.13.07.png

実行する

スクリーンショット 2020-02-26 12.08.17.png

実行をすると、パーティクルとして表示されているものがそれぞれ異なる位置から開始されました。
スクリーンショット 2020-02-26 12.22.55.png

このスクリプトを設定したプロジェクトはこちら
https://playcanv.as/p/9bfxB3nU/

GPU Instancing

こちらのコミットでGPU Instancingデモとソースコードが追加されました。

GPU Instancingについてはこちらの記事が参考になりました。
https://github.com/playcanvas/engine/commit/5f7c50102082cae250af8abf47d70b8636b1ccaa

GPU Instancingは、複数の「同一メッシュ」かつ「同一マテリアル」なオブジェクトがあった場合に、GPU側で一括で描画してくれる技術みたいです。
http://tsubakit1.hateblo.jp/entry/2016/06/28/233000

色々Examplesが追加

ARとVRのデモが多く追加されました。

※ 以上がv1.25.0のアップデート内容ですが、v1.25.1に今朝アップデートされていたのでそちらも追記致します

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

Promiseとasync/awaitの使い方

非同期で処理するためのPromise、async/awaitの基本的な使い方をまとめました。
詳しく知りたい方は参考文献をご確認ください。

Promise

Promiseオブジェクトを返す

return new Promise((resolve, reject) => {})

Promiseの結果を返す

resolveする

Promiseのresolveを実行する

return new Promise((resolve, reject) => {
  resolve('成功です')
})

rejectする

Promiseのrejectを実行する

return new Promise((resolve, reject) => {
  reject('失敗です')
})

async/await

処理の結果を待つ

async function sample() {
  const result = await samplePromise()
  console.log(result.data)
}

並列で処理する

async function sample() {
  const array =[5, 10, 15]
  const promiseAll = await Promise.all(
    array.map(async (value) => {
      return await samplePromise(value)
    })
  )

  return promiseAll
}

例外処理(エラーハンドリング)

async function errorHandling() {
  try {
    const result = await throwError()
    return result
  } catch (err) {
    throw err
  }
}

参考文献

この記事は以下の情報を参考にして執筆しました。

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

【Tesseract.js】ブラウザだけで画像内の文字を解析

Tesseract.jsは、画像解析ができるJavaScriptライブラリです。画像に書かれた文章を文字列に起こしてくれます。
最近使う機会があってなかなか良かったので、布教も兼ねてすぐに試せるコードをいくつか紹介します。
変更してみて欲しい箇所にはコード内にコメントしています。

ライブラリの基本的な使用法については公式ドキュメント他の記事で紹介されているので、そちらを参照ください。

画像例

この例ではLorem ipsumを解析します。
意味のない文章なので解析しづらいと思いますが、頑張ってもらいましょう。
image.png

1枚丸ごと解析

1枚の画像をブラウザに添付し、書いてある文字をすべて解析する例です。
以下のCDN使用例はhtmlファイルにコピペで動きます。
言語によって解析精度はかなり変わってくるので、色々な言語の画像で試してみてください。
…しかしLorem ipsumって何語で解析すれば良いんでしょうかね?元々意味がない文章とのことなので、ここでは日本語で進めます。

<html lang="ja">
  <body>
    <input type="file" id="image_zone">
    <div id="output"></div>
    <script src='https://unpkg.com/tesseract.js@v2.0.2/dist/tesseract.min.js'></script>
    <script>
      const imageZone = document.getElementById('image_zone')
      imageZone.addEventListener('change', resizePinnedImage, false)
      function resizePinnedImage(e) {
        const file = e.target.files[0]
        if (!file.type.match('image.*')) { return }
        Tesseract.recognize(
          file,
          'jpn', // 言語設定
          { logger: m => console.log(m) }
        ).then(({ data: { text } }) => {
          const out = document.getElementById('output')
          out.innerHTML = text
        })
      }
    </script>
  </body>
</html>

結果は以下のようになりました。
文字が繋がってしまっているので、画像を拡大したほうがよさそうです。次でやってみましょう。

Loem ipaundoorstamet coreeaeuradpscng eltseddoeuanod ierpornddguntutkkov etdoo magnaalqua_uienmad mimvenam qusrowud clatonulanco mboisneiuaiqdpexea commodocomsequat Dus aue ue coornrepeherdet Invoupaaeveltessoclun ooe eugdatnulaparauc cepeursntocuccatopdaatronpoden smtncupaquoWca desenntmottammigestkboum Cuablurpetuntndduniacus Nulagavdaociaodo_Nulanvaus upsqtcommodorhaevacaecsbbendunclrec MNcus magafal solgudn maus neoernmautseunbneuenodgevda Dusecteusotausvpuaevencua ponec kbots iusaelt lanterporUtulancopec leua cutenporcorgue wosesteuenoduupsidtnddunsapenrsusaquan Maccenasfermenun consequatn Doneciermenun Palencwaue maesuadanulaaml Dussanensen aiqetnec ommodo 0eb coreoquatqusyneque Aiquan eucbus gltutdcunaiguet elsnsiadpscngsapen sod naesuade danacuscoatem as molsscacisquenune Nlanacu_Alquam oonscqua Cusburaugueloemdapbusqus loeetat petunacnet Amneanmagnansl roNsqus nocsie cu_eugatinocLinhachabtasepaeadcuns、

画像を拡大して解析

以下はキャンバスを利用して画像を2倍に拡大して解析する例です。
Tesseractの対応形式は広く、Canvas要素をそのまま投入可能なのも便利なところです。

<html lang="ja">
  <body>
    <input type="file" id="image_zone">
    <br>
    <canvas id="canvas"></canvas>
    <div id="output"></div>
    <script src='https://unpkg.com/tesseract.js@v2.0.2/dist/tesseract.min.js'></script>
    <script>
      const imageZone = document.getElementById('image_zone')
      imageZone.addEventListener('change', resizePinnedImage, false)
      function resizePinnedImage(e) {
          const file = e.target.files[0]
          if (!file.type.match('image.*')) { return }
          resize(file)
      }
      function resize(file) {
        imageToCanvas(file).then(function (canvas) {
          Tesseract.recognize(
            canvas, // Canvasを投げられる!
            'jpn',
            { logger: m => console.log(m) }
          ).then(({ data: { text } }) => {
            const out = document.getElementById('output')
            out.innerHTML = text
          })
        }).catch(function (error) {
          console.error(error)
        })
      }

      function imageToCanvas (imageFile) {
        return new Promise(function (resolve, reject) {
          readImage(imageFile).then(function (src) {
            loadImage(src).then(function (image) {
              const canvas = document.getElementById("canvas")
              const ctx = canvas.getContext('2d')
              // スケール2倍
              const scale = 2
              canvas.width = image.width * scale
              canvas.height = image.height * scale
              ctx.drawImage(
                image, 
                (image.width - (canvas.width / scale)) / 2, (image.height - (canvas.height / scale)) / 2,
                canvas.width / scale, canvas.height / scale,
                0, 0,
                canvas.width, canvas.height
              )
              resolve(canvas)
            }).catch(function (error) {
              reject(error)
            })
          }).catch(function (error) {
            reject(error)
          })
        })
      }

      function readImage(image) {
        return new Promise(function (resolve, reject) {
          const reader = new FileReader()
          reader.onload = function () { resolve(reader.result) }
          reader.onerror = function (e) { reject(e) }
          reader.readAsDataURL(image)
        })
      }
      function loadImage(src) {
        return new Promise(function (resolve, reject) {
          const img = new Image()
          img.onload = function () { resolve(img) }
          img.onerror = function (e) { reject(e) }
          img.src = src
        })
      }
    </script>
  </body>
</html>

この例では、拡大後の画像がキャンバスに表示されます。(大きすぎるのでトリミングしました)
image.png
そして解析結果は以下のようになりました。
sやuの大文字・小文字違いを除けばほとんどが正確で、やはりちょうどよい文字の大きさがあるようです。

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cUpidatat non proident, SUunt in culpa qui officia deserunt mollit anim id est laborum. Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, eros est eUuismod turpis, id tincidunt sapien risus a dquam. Maecenas fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo edet, consedquat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam consedquat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, nisi Aenean magna nisl, mollis quis, molestie eu, feugliat in, orci. In hac habitasse platea dictumst.

画像の一部を切り取って解析

先ほどとほぼ同じですが、画像の真ん中を(この例では200px * 100px)切り取って解析する例です。
画像の必要のない部分をカットすることで、高速な解析が望めます。

<html lang="ja">
  <body>
    <input type="file" id="image_zone">
    <br>
    <canvas id="canvas"></canvas>
    <div id="output"></div>
    <script src='https://unpkg.com/tesseract.js@v2.0.2/dist/tesseract.min.js'></script>
    <script>
      const imageZone = document.getElementById('image_zone')
      imageZone.addEventListener('change', resizePinnedImage, false)
      function resizePinnedImage(e) {
          const file = e.target.files[0]
          if (!file.type.match('image.*')) { return }
          resize(file)
      }
      function resize(file) {
        imageToCanvas(file).then(function (canvas) {
          Tesseract.recognize(
            canvas,
            'jpn',
            { logger: m => console.log(m) }
          ).then(({ data: { text } }) => {
            const out = document.getElementById('output')
            out.innerHTML = text
          })
        }).catch(function (error) {
          console.error(error)
        })
      }

      function imageToCanvas (imageFile) {
        return new Promise(function (resolve, reject) {
          readImage(imageFile).then(function (src) {
            loadImage(src).then(function (image) {
              const canvas = document.getElementById("canvas")
              const ctx = canvas.getContext('2d')
              // 幅200、高さ100
              width = 200
              height = 100
              canvas.width = width
              canvas.height = height
              ctx.drawImage(image, (image.width / 2) - width / 2, (image.height / 2) + height / 2, width, height, 0, 0, width, height)
              resolve(canvas)
            }).catch(function (error) {
              reject(error)
            })
          }).catch(function (error) {
            reject(error)
          })
        })
      }

      function readImage(image) {
        return new Promise(function (resolve, reject) {
          const reader = new FileReader()
          reader.onload = function () { resolve(reader.result) }
          reader.onerror = function (e) { reject(e) }
          reader.readAsDataURL(image)
        })
      }
      function loadImage(src) {
        return new Promise(function (resolve, reject) {
          const img = new Image()
          img.onload = function () { resolve(img) }
          img.onerror = function (e) { reject(e) }
          img.src = src
        })
      }
    </script>
  </body>
</html>

雑に切り取ったら変なことになってしまいました。
個人的にはアプリ画面の必要な部分だけを切り取る用途に使う予定です。
image.png
以上です。ぜひ遊んでみてください。

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

脱・AtomicDesign - HTML+CSSコーディングの粒度分類法(HTML Parts)

はじめに

HTML+CSSコーディング専用の粒度分類を紹介します。
この仕組みは、デザインやワイヤーフレームなどの視覚情報を分解することに焦点をあて、分解した対象を部品化する流れも併せてガイド化した汎用手法です。
世の中には、コーポレートサイト / ポータルサイト / サービスサイト / システム管理画面 / ブログサイト… といった様々な種別のサイト、Webページがありますが、これらの「完成予想図(視覚情報)」を同じ方法で分解して、コーディング用の部品にできます。

粒度と言えばAtomicDesignが有名ですが、この「HTML Parts」も粒度そのものの考え方についてはAtomicDesignの踏襲です。その上で「思考の入り方・捉え方」や「名称と定義」をコーディング側に寄せることで、CSS設計やコーディング業務を標準化しやすくしています。

※この記事は標準化ノウハウ公開の一環として書いています。
仕組みの概要や前提事項などについては「UltimateCoding 概要・前提事項」のエントリをご確認ください。

前置きとAtomicDesign

そもそもAtomicDesignをコーディング視点で評するのはお角違いかもしれませんが、業界の一部では"万能薬"として組み入れる試みがあったのも事実です。それほど、画期的かつ魅力的な概念でした。主題に入るまえに「なぜ万能になり得なかったのか」を簡単に箇条書きします。

AtomicDesignの特徴

  • 小さなものから積み上げていく概念と、多くのコーディングで必要となる分解の行動が相反する
  • TemplatesとPagesが別の意味を喚起してしまい、そのままコーディングに使うと混乱する
  • サイト種別や規模によっては、図のA側に欲しい粒度が一つ不足することがある

といった所でしょうか。良い解決方法があるかのもしれませんが「そのままの状態で万能か?」と言われたら、そうではないでしょう。これは、AtomicDesignが不出来といいたいのではなく、専用に作られていないものを転用して使おうとしても、やはりどこか無理が出てくる。というお話です。

HTML Partsの概要

では、本題の「コーディング側から考える粒度分類」の話です。
HTML Partsは、以下のようにデザインやワイヤーフレームを「大きなものから小さなものへと分解」する事で粒度を分類します。

視覚情報の分解ステップ
この方法は、改めて言われなくとも既に多くの制作者が自然とおこなっているであろう流れ・ステップです。こういった一般的な流れの上にガイドを沿えることで、思考による変換や再構築を発生させず、負担がかかりにくいものとして提起します。

また、これがもしAtomicDesignによって「システマチックに作られたプロダクト」であったとしても、同じように上から綺麗に分解できることでしょう。
AtomicDesignで作られたものの分解

実用の見通しと説明内容

粒度の概念を実際のコーディングに実用するためには、以下の3つの内容把握と、Sassファイル管理の仕組みが必要となります。

  1. 視覚情報の分解方法 を把握
  2. 部品再利用設計の考え方 を把握
  3. 粒度の定義 を把握

以降、上記の順に従って説明します。
Sassファイルの管理方法については、最後に要点を述べた上で、今後別の記事で詳しく説明します。

1 ・ 視覚情報の分解方法

まず1つめの、具体的にどのように分解するのかについての説明です。
方法はいたってシンプルで、分解する対象である「完成予想図」を用意し、以下の図のように大きなものから4つの粒度によって分解していきます。

視覚情報の分解方法
図は企業系サイトを例にしていますが、サービス系サイト、ポータル系、システム管理画面、LP、ブログなど、どんな種別であっても「Webページの視覚情報」がある限り、分解手順そのものは同じです。

また、上記の図と、この記事の情報は、過去記事「1段上のCSS設計・コーディングの概念図」の内容と連動しています。この記事を単独で読んでも意味は通じますが、より良く利用したい場合には併せてご覧下さい。
HCDCモデル図
図との対応としては、左の2つの概念を実用するためのガイドが「HTML Parts」です。「Block」が今回のBlockと等価で、「Parts」が、今回のModule / Component / Elementに細分類されます。

2 ・ 部品再利用設計の考え方

粒度の具体的な説明に移る前に、重要な考え方・ポイントを先に説明します。
何かというと「粒度分類と実装は別で考える」という事です。ここを一緒にして捉えると後で混乱のもとになるため、先に2つの意味を説明します。

1 ・分類は必ず発生している

まず、分類そのものは常に発生しています。どういうことかというと、仮に、デザインやワイヤーフレームを紙に印刷して、ペンで四角い線を引いて分解しながら囲っていったとします。もうこれ以上分解できない。という所まで線をひくと、実質、紙の上に全ての粒度が洗い出されたことになります。
そして、特定の部分を見て、「AtomicDesignではこれはAtomsと呼ばれている」とか、今回の「HTML PartsであればElementと呼ばれている」というように、変わりようのない定義・事実として認識できます。

これが、分類は必ず発生している。という意味です。そして、この時点では実装に関する決めごとは何も発生していません。決める(設計する)のは制作者です。

2 ・どのように部品化するのか

次に、部品をどのようなコードで実装するか。という話です。
判断・意思決定するのは1つだけ。「その対象(部品)を、グローバルに再利用するかどうか」です。これが、CSS設計が大きく切り替わるポイントとなります。

「グローバルに再利用する」とは、以下のようなものです。

これは、CSS設計手法の「OOCSS」と同じ方向にある手法で、システム管理画面やサービス系サイトなどの「システマチックなサイト」で利用したくなるものです。

もし、上記のように再利用しないのであれば、その部品は以下のように「上位粒度の所有物」としてスタイリングします。


これらの図をテキストでまとめると以下のようになります。

1・単体でグローバルに再利用できるCSSを書く
図:スタイリングパターンA
2・上位粒度の所有物となるCSSを書く
図:スタイリングパターンB
図:スタイリングパターンC

そして、これらを「サイト全体視点」と「部品単独視点」の2つの軸で判断していくことが、「CSS設計」の中核であり重要ポイントになります。

これらの前提を踏まえた上で、以下から粒度の説明をおこないます。

3 ・ 粒度の定義把握

各粒度の詳細説明です。
それぞれに細かな定義や判断基準などを記載していますが、先述の通り「粒度分類と実装は別」であり、これらはあくまで制作のためのガイドです。制作者の判断以上に強い立場ではありません。

レギュレーションによって「完全遵守・例外は認めない」と定めるなら話は別ですが、そうではないなら「その視覚情報をどのようにコード管理したいのか」という、制作者の意思・意図を優先し、"ねばならない"の思考に捕らわれることがないようご注意下さい。

1 ・ Block

Block(ブロック)は、情報の意味や役割的に自立・独立した領域や区画・塊です。
機能・役割・情報属性などの基準によって、制作者が「これ以上は他と同じにできない」と判断した範囲によって切り出します。

Block参考イメージ

多くの場合はdivによるコンテナに名前が与えられます。

Blockの粒度判断

粒度の判断は、主たる情報である「コンテンツ」と、ヘッダー・フッターなどの「フレーム」部分とで分けて考えることができます。具体的には以下のようになります。

  • コンテンツ
    • article section asideといったタグでマークアップできそうな範囲
    • 見出しに使うhタグを含むもの(文書上の暗示・省略されたものも含む)
    • 任意の構造体が入りそうなもの
  • フレーム
    • ルートのheader main footerや、nav の領域

また、これらは、プロジェクトやデザインの文脈によって解釈が異なるというのはあり得る話です。
例えば、同じブランディングエリアであっても、ヘッダーバーの中に小さくあるものと、ブラウザ全面に画像や動画が敷かれ、数点のリンクと共に中央に配されるようなブランディングエリアとでは解釈・役割が異なるでしょう。

Blockの部品作成方法

block/にSassファイルとして切り出してCSSを管理します。
もし、Blockの内部部品に「下位粒度のグローバルな再利用部品」(スタイリングパターンAで作られた部品)を設置する場合は、Blockはそれらの部品を設置するための枠組みのような役割にとどまるでしょう。
逆に、Block内部の部品を自分の一部としてスタイリングする場合は、先述のスタイリングパターンB・Cどちらかでスタイリングすることになります。

Blockのユースケース

コーポレートやブログ、ブランディング、LPなどの情報発信系サイトであれば、よほどの規模ではない限り、この分類でコード化は完成するのではないかと思います。
内部部品はスタイリングパターンB/C(※近年の慣例手法はCのBEM式)のみで作成し、同じ部品が何度か登場したとしても、パターンAの代わりに、SassのExtendやMixin機能で同一コードを流用して完成させるような方法です。

参考情報

余談・参考情報になりますが、bodyタグ直下に機能的・慣例的に設置する.wrapperといった命名の「全体包括要素」もBlockであると捉えると、「Block同士は恒常的にネストする」という意味が理解しやすくなります。※このとき、思考のつじつまが合いやすい命名は.siteです。

<body>
    <div class="site"><!--この中にあるのは「サイト」の表示用データ-->
        <div class="site-header"> <header></header> </div>
        <div class="site-main"> <main></main> </div>
        <div class="site-footer"> <footer></footer> </div>
    </div>
</body>

また、コンテンツのBlockも同じ構造を意識してマークアップすることで、サイト全体のコードがフラクタル構造(自己相似)となり、ルールが捉えやすくなります。(※サンプルはBEM記法で記載)

<div class="photo-gallery"><!--この中にあるのは「写真ギャラリー」表示用のデータ-->
    <section>
        <div class="photo-gallery__header"> <header></header> </div>
        <div class="photo-gallery__body"></div>
        <div class="photo-gallery__footer"> <footer></footer> </div>
    </section>
</div>

2 ・ Module

Module(モジュール)は、単体でも機能しそうな中程度の情報の塊です。
カードや、ギャラリーの1区画。会員証を模したようなもの、プロフィールのひと塊、ECサイトの商品一覧、メニューナビゲーションの1区画などが該当するでしょう。

Module参考イメージ

多くの場合はdivによるコンテナに名前が与えられます。

Moduleの粒度判断

粒度の判断は、コンテンツ部分とフレーム部分で分けて考えられます。
下位粒度のcomponentが集まったものと捉えるとイメージしやすいかもしれません。

  • コンテンツ
    • Blockほどの情報の規模感は無いが、単独でも機能しそうな視覚情報の塊
    • 場合によっては任意の構造体が入りそうなもの
    • その場でリピートして使われそうなもの
  • フレーム
    • ブランディングエリアや、コンタクト情報 等
    • ブランディングエリアや、サイトマップリンク、コンタクト情報 等

Moduleの部品作成方法

自身をグローバルな再利用部品として管理したい時に、module/ディレクトリに切り出して管理します。そうではないなら、この粒度のものは、上位粒度の持ち物としてスタイリングが完了しているはずです。

Moduleのユースケース/この粒度は必要か

ModuleはBlockと近い存在となります。特にBEM記法を使っているとBEMのBlockの概念と相まって「粒度が違えば全てModuleディレクトリに切り出さないといけないのか?」となる事もあるでしょう。そういった管理が煩雑になる場合は、Blockとして管理しても何も問題ありません。(究極は、制作者が混乱しなければよいのです)

では、定義の意味は?

「であれば結局は無法地帯では?」と思われるかもしれませんが、そうではありません。どんな時にこの粒度が有用かというと、1つは再利用の明示です。
Blockは最上位にあるため、どのように再利用しているのか、もしくはしていないのかはHTMLファイルを要調査。となりますが、Moduleディレクトリに置いただけで「どこかで必ず再利用されている中粒度部品」もしくは「どこで再利用してもよい中粒度部品」として意識づけできます。
最初にBlockのディレクトリにSassファイルを置いていても、後から上記のように明示したくなった場合は移動させることもできます。

システム開発との連動

もう1つ有用なパターンは、ECサイトやサービスサイトなどのシステムが絡む時です。
システム側のフロントテンプレートなどにおいて「モジュール」として切り出すものは、この粒度と同じ規模感になるものも多くあります。それぞれ同じModuleという名称のディレクトリで管理すれば、双方が意識上対応したものとして管理できます。

このように、実用時には必要に応じて使い分けられる粒度となります。

3 ・ Component

Component(コンポーネント)とは、見出しやボタン、リストや表組などの、単体でもその部品の機能や目的・情報が分かる小さな要素です。
ModuleやBlockから見れば、自分達を構成する小さな部品群で、下位粒度のElementのから見た時には、Elementの組み合わせがComponentであると表現できます。

Component参考イメージ

この粒度のものには「構造体」や「文書構造」が入ることはありません。
divspanのラッパーに名前を与えることもあれば、ul ol tableなどに名前を与えることもあるでしょう。

Componentの粒度判断

文書構造や構造体が入る余地・感覚が無いもので、かつ、以下の1~4のうち、1つでも当てはまればComponentとなります。

  • 視覚情報
    • 1・視覚情報として分割できる
  • HTMLコード
    • 2・HTMLタグがHTMLタグでラップされている
    • 3・内部を自然に拡張できる
    • 4・コードとして分割・分離できる

また、レンダリング結果はElementと同一になるが、コード側から見ればComponentと判断できるものがあります。以下のようなパターンです。

コード側から見てComponentになる例

imgタグ単体は後述するElementにあたりますが、これを<div class="image">などでラップすれば、.imageは立派なComponentです。「分解・分離できる状態」であり、後に内部にキャプションを入れるなど、「自然に拡張できる状態」になっています。

同様に、pタグだけの文章はElementの粒度にあたりますが、これを<div class="text_box">といったような命名のボックスでラップすれば、同様にComponentとなります。文章が追加されても、新たなpタグによって内部を自然と拡張できます。

Componentの部品作成方法

自身をグローバルな再利用部品としたい場合に、component/ディレクトリに切り出して管理します。そうではないなら、この粒度のものは、上位粒度の持ち物としてスタイリングが完了しているはずです。

Componentのユースケース

Componentをグローバルな再利用部品として作成・運用したくなるのは、システム管理画面やシングルアプリケーション系のプロダクト、ブログの記事内の部品、サービスサイトのUI部品などでしょう。
OOCSSの概念と併用する事が多くなるでしょうし、"既製品"としてBootstrapのようなCSSフレームワークを利用する事になるのかもしれません。

小規模でシステマチックではない静的サイトであれば、Blockの項に記載したとおり、Componentは全て上位粒度の持ち物としてスタイリングした方が効率よく管理できるでしょう。

部品の集まりをどう捉えるか

この粒度のものは、幾つかの部品が集まり特定の機能になる場合もあります。例えば、ボタンが複数横に並んで連結されていれるような場合です。これはComponentの集まりである。と捉える事もできますし、divでラップして「ナビゲーション」という意味の名前を与えればModuleにも、Blockにもできます。
つまり、そのプロジェクトにおいて、対象の部品をどのように管理したいか。という制作者の意思・意図が第一優先です。それをサポートするように、振り分けできる手立てが用意されている。という事になります。

参考情報

Componentは特性として2つの属性のものがあります。
1つは、テーブル(table)やリスト(ul,ol,dl)など、HTMLの仕様により内部構造が定められているものです。これらは、HTMLの仕様に従い内部を拡張できますが、使えるタグは限られています。

もう1つは、「アイコン+タイトル=メニュー項目」「ラジオボタン+テキスト=選択部品」とするなど、制作者の意図により小さな部品を組み合わせてComponentとしたものです。

※この区分けは粒度分類には影響しません。属性的に2つのものがあるという参考情報です。

4 ・ Element

Element(エレメント)は、表示上の面積に関わらず、視覚情報上もしくはコード上でもそれ以上部品分解できない最小の要素です。
Elementの組み合わせ、もしくは単体を何らかの要素でラップすれば、Componentとなります。

Element参考イメージ

imginputなど、直接レンダリングされるタグやspaniなどに名前を与える事があるでしょう。

Elementの粒度判断

それ以上分割分割できない要素で、一行のテキストや、ラジオボタン、チェックボックスなどです。無理に分割しようとすると、意図した情報伝達ができない、機能不全になる、コードとして成立しないといった不都合が起きるようなものが該当します。
また、粒度はレンダリング時の面積にとは関係がありません。いくら面積の広い画像を配置しようとも、imgタグレベルは一様にElementの粒度となります。どれだけ巨大なアイコンや、ラジオボタンを配置したとしても同じ事が言えます。

Elementの部品作成

自身をグローバルな再利用部品としたい場合に、element/ディレクトリに切り出して管理します。そうではないなら、この粒度のものは、上位粒度の持ち物としてスタイリングが完了しているはずです。

Elementのユースケース

この粒度をグローバルな再利用部品として作成・運用するとなると、独自にCSSフレームワークを作る。もしくは、ドラッグ&ドロップでサイトを作っていくようなサービスを構築する時でしょうか。※他にも用途はあるかもしれません

その他

プレフィックス

実用する際には、これらに名前を与えてからCSSプロパティを書くことになりますが、命名時のプレフィックスのルールはここでは定めません。(ガイドという立場として、特に奨励も制限もしません)
制作の必要性に応じて「再利用前提のものは m- c- e- を付与する」など、それぞれ使い勝手が良いように自由にルールを制定してください。

アイデア

これはまだ試せていませんが、1つのアイデアとして、この粒度分類を逆にデザインシステム側に反映できるかもしれません。コーディング側で最適化したものをデザインシステムに乗せられたなら、スムーズに連携できる可能性はあります。

粒度の総括

以上、4つの粒度をコーディングの側面から定義しました。
分類そのものの本質部分を見ればAtomicDesignのそれと非常に近いものですが、思考の入り方と語句の定義を変えただけで、人の認識や使用感は異なったものになります。
では、これが万人向けするのか。といえば、それは分かりません。しかし、少なくとも1つの組織と協力会社において使用する限り、破綻などの問題がゼロであることは事実です。

HCDCモデル図と今回の「HTML Parts」、そして、次回公開する「Sassファイルディレクトリ管理手法」によって、より多くのサイト種別に対応した設計がおこないやすくなります。

Sassファイル・ディレクトリ管理

これらの粒度の名称は、そのままSassファイル管理ディレクトリの名称として利用することになります。
部品化の方法としては、先述の通り「グローバルに再利用する場合」のみ、その粒度のディレクトリに部品を切り出して管理します。

しかし、これらを含めた全体をどのように管理するかは、また別の話となります。これはCSS設計の命題のひとつであり、概念を実務利用するための重要事項となります。

公開予定の手法
次回紹介するものは、プロジェクトごとにカスタマイズ可能なSassファイル管理の仕組みです。標準ルールを中心として、同じロジックでいくつものサイトタイプに柔軟に対応できます。

この記事作成時点での実務利用実績は以下の通りです。
コーポレートサイト / ポータルサイト / サービスサイト / システム管理画面 / ブログサイト / ECサイト(オンラインショップ) / ランディングページ(LP) / イントラサイト

この管理方法についての情報は、投稿次第こちらの記事からもリンクします。

関連記事

クレジット・その他

Ultimate Coding
概要・前提事項

この仕組みは、組織所属時に業務効率化のために構築したものであり、許可を得た上で設計者本人が個人活動として公開しています。商用の制作や開発には利用していただけますが、仕組みを販売したり媒体化するなどの、制作以外での商用化はご遠慮下さい。質問その他、何かあれば@croco_worksまでお声かけください。

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

脱・Atomic Design - HTML+CSSコーディングの粒度分類法(HTML Parts)

はじめに

HTML+CSSコーディング専用の粒度分類を紹介します。
この仕組みは、デザインやワイヤーフレームなどの視覚情報を分解することに焦点をあて、分解した対象を部品化する流れも併せてガイド化した汎用手法です。
世の中には、コーポレートサイト / ポータルサイト / サービスサイト / システム管理画面 / ブログサイト… といった様々な種別のサイト、Webページがありますが、これらの「完成予想図(視覚情報)」を同じ方法で分解して、コーディング用の部品にできます。

粒度と言えばAtomic Designが有名ですが、この「HTML Parts」も粒度そのものの考え方についてはAtomic Designの踏襲です。その上で「思考の入り方・捉え方」や「名称と定義」をコーディング側に寄せることで、CSS設計やコーディング業務を標準化しやすくしています。

※この記事は標準化ノウハウ公開の一環として書いています。
仕組みの概要や前提事項などについては「UltimateCoding 概要・前提事項」のエントリをご確認ください。

前置きとAtomic Design

そもそもAtomic Designをコーディング視点で評するのはお角違いかもしれませんが、業界の一部では"万能薬"として組み入れる試みがあったのも事実です。それほど、画期的かつ魅力的な概念でした。主題に入るまえに「なぜ万能になり得なかったのか」を簡単に箇条書きします。

Atomic Designの特徴

  • 小さなものから積み上げていく概念と、多くのコーディングで必要となる分解の行動が相反する
  • TemplatesとPagesが別の意味を喚起してしまい、そのままコーディングに使うと混乱する
  • サイト種別や規模によっては、図のA側に欲しい粒度が一つ不足することがある

といった所でしょうか。良い解決方法があるかのもしれませんが「そのままの状態で万能か?」と言われたら、そうではないでしょう。これは、Atomic Designが不出来といいたいのではなく、専用に作られていないものを転用して使おうとしても、やはりどこか無理が出てくる。というお話です。

HTML Partsの概要

では、本題の「コーディング側から考える粒度分類」の話です。
HTML Partsは、以下のようにデザインやワイヤーフレームを「大きなものから小さなものへと分解」する事で粒度を分類します。

視覚情報の分解ステップ
この方法は、改めて言われなくとも既に多くの制作者が自然とおこなっているであろう流れ・ステップです。こういった一般的な流れの上にガイドを沿えることで、思考による変換や再構築を発生させず、負担がかかりにくいものとして提起します。

また、これがもしAtomic Designによって「システマチックに作られたプロダクト」であったとしても、同じように上から綺麗に分解できることでしょう。
Atomic Designで作られたものの分解

実用の見通しと説明内容

粒度の概念を実際のコーディングに実用するためには、以下の3つの内容把握と、Sassファイル管理の仕組みが必要となります。

  1. 視覚情報の分解方法 を把握
  2. 部品再利用設計の考え方 を把握
  3. 粒度の定義 を把握

以降、上記の順に従って説明します。
Sassファイルの管理方法については、最後に要点を述べた上で、今後別の記事で詳しく説明します。

1 ・ 視覚情報の分解方法

まず1つめの、具体的にどのように分解するのかについての説明です。
方法はいたってシンプルで、分解する対象である「完成予想図」を用意し、以下の図のように大きなものから4つの粒度によって分解していきます。

視覚情報の分解方法
図は企業系サイトを例にしていますが、サービス系サイト、ポータル系、システム管理画面、LP、ブログなど、どんな種別であっても「Webページの視覚情報」がある限り、分解手順そのものは同じです。

また、上記の図と、この記事の情報は、過去記事「1段上のCSS設計・コーディングの概念図」の内容と連動しています。この記事を単独で読んでも意味は通じますが、より良く利用したい場合には併せてご覧下さい。
HCDCモデル図
図との対応としては、左の2つの概念を実用するためのガイドが「HTML Parts」です。「Block」が今回のBlockと等価で、「Parts」が、今回のModule / Component / Elementに細分類されます。

2 ・ 部品再利用設計の考え方

粒度の具体的な説明に移る前に、重要な考え方・ポイントを先に説明します。
何かというと「粒度分類と実装は別で考える」という事です。ここを一緒にして捉えると後で混乱のもとになるため、先に2つの意味を説明します。

1 ・分類は必ず発生している

まず、分類そのものは常に発生しています。どういうことかというと、仮に、デザインやワイヤーフレームを紙に印刷して、ペンで四角い線を引いて分解しながら囲っていったとします。もうこれ以上分解できない。という所まで線をひくと、実質、紙の上に全ての粒度が洗い出されたことになります。
そして、特定の部分を見て、「AtomicDesignではこれはAtomsと呼ばれている」とか、今回の「HTML PartsであればElementと呼ばれている」というように、変わりようのない定義・事実として認識できます。

これが、分類は必ず発生している。という意味です。そして、この時点では実装に関する決めごとは何も発生していません。決める(設計する)のは制作者です。

2 ・どのように部品化するのか

次に、部品をどのようなコードで実装するか。という話です。
判断・意思決定するのは1つだけ。「その対象(部品)を、グローバルに再利用するかどうか」です。これが、CSS設計が大きく切り替わるポイントとなります。

「グローバルに再利用する」とは、以下のようなものです。

これは、CSS設計手法の「OOCSS」と同じ方向にある手法で、システム管理画面やサービス系サイトなどの「システマチックなサイト」で利用したくなるものです。

もし、上記のように再利用しないのであれば、その部品は以下のように「上位粒度の所有物」としてスタイリングします。


これらの図をテキストでまとめると以下のようになります。

1・単体でグローバルに再利用できるCSSを書く
図:スタイリングパターンA
2・上位粒度の所有物となるCSSを書く
図:スタイリングパターンB
図:スタイリングパターンC

そして、これらを「サイト全体視点」と「部品単独視点」の2つの軸で判断していくことが、「CSS設計」の中核であり重要ポイントになります。

これらの前提を踏まえた上で、以下から粒度の説明をおこないます。

3 ・ 粒度の定義把握

各粒度の詳細説明です。
それぞれに細かな定義や判断基準などを記載していますが、先述の通り「粒度分類と実装は別」であり、これらはあくまで制作のためのガイドです。制作者の判断以上に強い立場ではありません。

レギュレーションによって「完全遵守・例外は認めない」と定めるなら話は別ですが、そうではないなら「その視覚情報をどのようにコード管理したいのか」という、制作者の意思・意図を優先し、"ねばならない"の思考に捕らわれることがないようご注意下さい。

1 ・ Block

Block(ブロック)は、情報の意味や役割的に自立・独立した領域や区画・塊です。
機能・役割・情報属性などの基準によって、制作者が「これ以上は他と同じにできない」と判断した範囲によって切り出します。

Block参考イメージ

多くの場合はdivによるコンテナに名前が与えられます。

Blockの粒度判断

粒度の判断は、主たる情報である「コンテンツ」と、ヘッダー・フッターなどの「フレーム」部分とで分けて考えることができます。具体的には以下のようになります。

  • コンテンツ
    • article section asideといったタグでマークアップできそうな範囲
    • 見出しに使うhタグを含むもの(文書上の暗示・省略されたものも含む)
    • 任意の構造体が入りそうなもの
  • フレーム
    • ルートのheader main footerや、nav の領域

また、これらは、プロジェクトやデザインの文脈によって解釈が異なるというのはあり得る話です。
例えば、同じブランディングエリアであっても、ヘッダーバーの中に小さくあるものと、ブラウザ全面に画像や動画が敷かれ、数点のリンクと共に中央に配されるようなブランディングエリアとでは解釈・役割が異なるでしょう。

Blockの部品作成方法

block/にSassファイルとして切り出してCSSを管理します。
もし、Blockの内部部品に「下位粒度のグローバルな再利用部品」(スタイリングパターンAで作られた部品)を設置する場合は、Blockはそれらの部品を設置するための枠組みのような役割にとどまるでしょう。
逆に、Block内部の部品を自分の一部としてスタイリングする場合は、先述のスタイリングパターンB・Cどちらかでスタイリングすることになります。

Blockのユースケース

コーポレートやブログ、ブランディング、LPなどの情報発信系サイトであれば、よほどの規模ではない限り、この分類でコード化は完成するのではないかと思います。
内部部品はスタイリングパターンB/C(※近年の慣例手法はCのBEM式)のみで作成し、同じ部品が何度か登場したとしても、パターンAの代わりに、SassのExtendやMixin機能で同一コードを流用して完成させるような方法です。

参考情報

余談・参考情報になりますが、bodyタグ直下に機能的・慣例的に設置する.wrapperといった命名の「全体包括要素」もBlockであると捉えると、「Block同士は恒常的にネストする」という意味が理解しやすくなります。※このとき、思考のつじつまが合いやすい命名は.siteです。

<body>
    <div class="site"><!--この中にあるのは「サイト」の表示用データ-->
        <div class="site-header"> <header></header> </div>
        <div class="site-main"> <main></main> </div>
        <div class="site-footer"> <footer></footer> </div>
    </div>
</body>

また、コンテンツのBlockも同じ構造を意識してマークアップすることで、サイト全体のコードがフラクタル構造(自己相似)となり、ルールが捉えやすくなります。(※サンプルはBEM記法で記載)

<div class="photo-gallery"><!--この中にあるのは「写真ギャラリー」表示用のデータ-->
    <section>
        <div class="photo-gallery__header"> <header></header> </div>
        <div class="photo-gallery__body"></div>
        <div class="photo-gallery__footer"> <footer></footer> </div>
    </section>
</div>

2 ・ Module

Module(モジュール)は、単体でも機能しそうな中程度の情報の塊です。
カードや、ギャラリーの1区画。会員証を模したようなもの、プロフィールのひと塊、ECサイトの商品一覧、メニューナビゲーションの1区画などが該当するでしょう。

Module参考イメージ

多くの場合はdivによるコンテナに名前が与えられます。

Moduleの粒度判断

粒度の判断は、コンテンツ部分とフレーム部分で分けて考えられます。
下位粒度のcomponentが集まったものと捉えるとイメージしやすいかもしれません。

  • コンテンツ
    • Blockほどの情報の規模感は無いが、単独でも機能しそうな視覚情報の塊
    • 場合によっては任意の構造体が入りそうなもの
    • その場でリピートして使われそうなもの
  • フレーム
    • ブランディングエリアや、コンタクト情報 等
    • ブランディングエリアや、サイトマップリンク、コンタクト情報 等

Moduleの部品作成方法

自身をグローバルな再利用部品として管理したい時に、module/ディレクトリに切り出して管理します。そうではないなら、この粒度のものは、上位粒度の持ち物としてスタイリングが完了しているはずです。

Moduleのユースケース/この粒度は必要か

ModuleはBlockと近い存在となります。特にBEM記法を使っているとBEMのBlockの概念と相まって「粒度が違えば全てModuleディレクトリに切り出さないといけないのか?」となる事もあるでしょう。そういった管理が煩雑になる場合は、Blockとして管理しても何も問題ありません。(究極は、制作者が混乱しなければよいのです)

では、定義の意味は?

「であれば結局は無法地帯では?」と思われるかもしれませんが、そうではありません。どんな時にこの粒度が有用かというと、1つは再利用の明示です。
Blockは最上位にあるため、どのように再利用しているのか、もしくはしていないのかはHTMLファイルを要調査。となりますが、Moduleディレクトリに置いただけで「どこかで必ず再利用されている中粒度部品」もしくは「どこで再利用してもよい中粒度部品」として意識づけできます。
最初にBlockのディレクトリにSassファイルを置いていても、後から上記のように明示したくなった場合は移動させることもできます。

システム開発との連動

もう1つ有用なパターンは、ECサイトやサービスサイトなどのシステムが絡む時です。
システム側のフロントテンプレートなどにおいて「モジュール」として切り出すものは、この粒度と同じ規模感になるものも多くあります。それぞれ同じModuleという名称のディレクトリで管理すれば、双方が意識上対応したものとして管理できます。

このように、実用時には必要に応じて使い分けられる粒度となります。

3 ・ Component

Component(コンポーネント)とは、見出しやボタン、リストや表組などの、単体でもその部品の機能や目的・情報が分かる小さな要素です。
ModuleやBlockから見れば、自分達を構成する小さな部品群で、下位粒度のElementのから見た時には、Elementの組み合わせがComponentであると表現できます。

Component参考イメージ

この粒度のものには「構造体」や「文書構造」が入ることはありません。
divspanのラッパーに名前を与えることもあれば、ul ol tableなどに名前を与えることもあるでしょう。

Componentの粒度判断

文書構造や構造体が入る余地・感覚が無いもので、かつ、以下の1~4のうち、1つでも当てはまればComponentとなります。

  • 視覚情報
    • 1・視覚情報として分割できる
  • HTMLコード
    • 2・HTMLタグがHTMLタグでラップされている
    • 3・内部を自然に拡張できる
    • 4・コードとして分割・分離できる

また、レンダリング結果はElementと同一になるが、コード側から見ればComponentと判断できるものがあります。以下のようなパターンです。

コード側から見てComponentになる例

imgタグ単体は後述するElementにあたりますが、これを<div class="image">などでラップすれば、.imageは立派なComponentです。「分解・分離できる状態」であり、後に内部にキャプションを入れるなど、「自然に拡張できる状態」になっています。

同様に、pタグだけの文章はElementの粒度にあたりますが、これを<div class="text_box">といったような命名のボックスでラップすれば、同様にComponentとなります。文章が追加されても、新たなpタグによって内部を自然と拡張できます。

Componentの部品作成方法

自身をグローバルな再利用部品としたい場合に、component/ディレクトリに切り出して管理します。そうではないなら、この粒度のものは、上位粒度の持ち物としてスタイリングが完了しているはずです。

Componentのユースケース

Componentをグローバルな再利用部品として作成・運用したくなるのは、システム管理画面やシングルアプリケーション系のプロダクト、ブログの記事内の部品、サービスサイトのUI部品などでしょう。
OOCSSの概念と併用する事が多くなるでしょうし、"既製品"としてBootstrapのようなCSSフレームワークを利用する事になるのかもしれません。

小規模でシステマチックではない静的サイトであれば、Blockの項に記載したとおり、Componentは全て上位粒度の持ち物としてスタイリングした方が効率よく管理できるでしょう。

部品の集まりをどう捉えるか

この粒度のものは、幾つかの部品が集まり特定の機能になる場合もあります。例えば、ボタンが複数横に並んで連結されていれるような場合です。これはComponentの集まりである。と捉える事もできますし、divでラップして「ナビゲーション」という意味の名前を与えればModuleにも、Blockにもできます。
つまり、そのプロジェクトにおいて、対象の部品をどのように管理したいか。という制作者の意思・意図が第一優先です。それをサポートするように、振り分けできる手立てが用意されている。という事になります。

参考情報

Componentは特性として2つの属性のものがあります。
1つは、テーブル(table)やリスト(ul,ol,dl)など、HTMLの仕様により内部構造が定められているものです。これらは、HTMLの仕様に従い内部を拡張できますが、使えるタグは限られています。

もう1つは、「アイコン+タイトル=メニュー項目」「ラジオボタン+テキスト=選択部品」とするなど、制作者の意図により小さな部品を組み合わせてComponentとしたものです。

※この区分けは粒度分類には影響しません。属性的に2つのものがあるという参考情報です。

4 ・ Element

Element(エレメント)は、表示上の面積に関わらず、視覚情報上もしくはコード上でもそれ以上部品分解できない最小の要素です。
Elementの組み合わせ、もしくは単体を何らかの要素でラップすれば、Componentとなります。

Element参考イメージ

imginputなど、直接レンダリングされるタグやspaniなどに名前を与える事があるでしょう。

Elementの粒度判断

それ以上分割分割できない要素で、一行のテキストや、ラジオボタン、チェックボックスなどです。無理に分割しようとすると、意図した情報伝達ができない、機能不全になる、コードとして成立しないといった不都合が起きるようなものが該当します。
また、粒度はレンダリング時の面積にとは関係がありません。いくら面積の広い画像を配置しようとも、imgタグレベルは一様にElementの粒度となります。どれだけ巨大なアイコンや、ラジオボタンを配置したとしても同じ事が言えます。

Elementの部品作成

自身をグローバルな再利用部品としたい場合に、element/ディレクトリに切り出して管理します。そうではないなら、この粒度のものは、上位粒度の持ち物としてスタイリングが完了しているはずです。

Elementのユースケース

この粒度をグローバルな再利用部品として作成・運用するとなると、独自にCSSフレームワークを作る。もしくは、ドラッグ&ドロップでサイトを作っていくようなサービスを構築する時でしょうか。※他にも用途はあるかもしれません

その他

プレフィックス

実用する際には、これらに名前を与えてからCSSプロパティを書くことになりますが、命名時のプレフィックスのルールはここでは定めません。(ガイドという立場として、特に奨励も制限もしません)
制作の必要性に応じて「再利用前提のものは m- c- e- を付与する」など、それぞれ使い勝手が良いように自由にルールを制定してください。

アイデア

これはまだ試せていませんが、1つのアイデアとして、この粒度分類を逆にデザインシステム側に反映できるかもしれません。コーディング側で最適化したものをデザインシステムに乗せられたなら、スムーズに連携できる可能性はあります。

粒度の総括

以上、4つの粒度をコーディングの側面から定義しました。
分類そのものの本質部分を見ればAtomic Designのそれと非常に近いものですが、思考の入り方と語句の定義を変えただけで、人の認識や使用感は異なったものになります。
では、これが万人向けするのか。といえば、それは分かりません。しかし、少なくとも1つの組織と協力会社において使用する限り、破綻などの問題がゼロであることは事実です。

HCDCモデル図と今回の「HTML Parts」、そして、次回公開する「Sassファイルディレクトリ管理手法」によって、より多くのサイト種別に対応した設計がおこないやすくなります。

Sassファイル・ディレクトリ管理

これらの粒度の名称は、そのままSassファイル管理ディレクトリの名称として利用することになります。
部品化の方法としては、先述の通り「グローバルに再利用する場合」のみ、その粒度のディレクトリに部品を切り出して管理します。

しかし、これらを含めた全体をどのように管理するかは、また別の話となります。これはCSS設計の命題のひとつであり、概念を実務利用するための重要事項となります。

公開予定の手法
次回紹介するものは、プロジェクトごとにカスタマイズ可能なSassファイル管理の仕組みです。標準ルールを中心として、同じロジックでいくつものサイトタイプに柔軟に対応できます。

この記事作成時点での実務利用実績は以下の通りです。
コーポレートサイト / ポータルサイト / サービスサイト / システム管理画面 / ブログサイト / ECサイト(オンラインショップ) / ランディングページ(LP) / イントラサイト

この管理方法についての情報は、投稿次第こちらの記事からもリンクします。

関連記事

クレジット・その他

Ultimate Coding
概要・前提事項

この仕組みは、組織所属時に業務効率化のために構築したものであり、許可を得た上で設計者本人が個人活動として公開しています。商用の制作や開発には利用していただけますが、仕組みを販売したり媒体化するなどの、制作以外での商用化はご遠慮下さい。質問その他、何かあれば@croco_worksまでお声かけください。

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

JavaScriptの連想配列に関する文法

const customer = {"name": "taro", "age": 22};
const {name, age} = customer;

console.log(name);
console.log(age);

// output 
// "taro"
// "age"
const name = "taro";
const age = 22;

console.log({name,age}):
// output
// {"name": "taro", "age": 22}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cognito+Amplifyで二要素認証する(ログイン画面で ID/PW/"二要素用のコード値" を入力して認証する)

概要

  • Cognito+Amplifyで二要素認証する。
  • ログイン画面に ID/PW/コード値(二要素目扱い) の3項目を入力することを前提とする
    ※なので、二段階認証では無い・・・
  • codeにはGoogle Authenticator等で生成されるコード値が入る前提とする

実装

Amplify側

下記でサインインする。
※二要素目のパラメータとして validationData を送信する。

Auth.signIn({"username": user_id, "password": user_password, "validationData": { "code": "WinAuth_code" }})
// ※Cognitoの認証前トリガーでcodeがとれる

Amplify.signIn() のIF参照

Amplify.signIn() の第1パラメータを SignInOpts 形式で指定している

Cognito側

認証前トリガーを使って、validationDatacode を取得し、コードの検証をする形になる。

認証前トリガーのLambda

import boto3

def lambda_handler(event, context):

    # Usernameに紐づくグループを取得するサンプルコード
    client = boto3.client('cognito-idp')
    response = client.admin_list_groups_for_user(UserPoolId=event['userPoolId'], Username=event['userName'] ) 

    print("---- ---- ---- ---- ---- ---- ---- ")
    print(event)
    print(response)
    print("---- ---- ---- ---- ---- ---- ---- ")

    # コード検証を実行 ※ソースは省略
    # エラーの条件によってメッセージを変える
    # ※
    # エラー時にメッセージを変更することしかできない(HTTPのステータスを変えるとかは出来ない)ので、
    # コードが誤っている場合は専用のメッセージをAmplify側に返すことになる
    raise Exception("Cannot authenticate users from this user pool app client")  

    return event

認証前トリガーLambdaのログ(Lambdaハンドラ引数の event の中身)

下記に注目

  • event['username']に入力したユーザネームが来ている
  • event['request']['validationData'] が来ている
{'callerContext': {'awsSdkVersion': 'aws-sdk-unknown-unknown',
                   'clientId': 'xxxxxxxxxxxxxxxxxxxxxxxxxx'},
 'region': 'ap-northeast-1',
 'request': {'userAttributes': {'cognito:token_nbf': '1582593027733',
                                'cognito:user_status': 'CONFIRMED',
                                'sub': '67eb1ed1-240b-4861-9844-f82ebc08d2f8'},
             'userNotFound': False,
             'validationData': {'aaaa': '12345'}},
 'response': {},
 'triggerSource': 'PreAuthentication_Authentication',
 'userName': 'hogehoge',
 'userPoolId': 'ap-northeast-1_xxxxxxxxx',
 'version': '1'}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptの便利関数

あったら便利な汎用的javascript関数をまとめました。

deepメソッド

ネストしたオブジェクトにアクセスするときに使用すると、存在チェックが出来る

const deep = (obj, path) => {
  path = path.split(/\.|\[|\]/).filter(v => v)
  for (const k in path) {
    if (!obj) return false
    else if (obj[path[k]]) obj = obj[path[k]]
    else return false
  }
  return true
}

使い方

testオブジェクトに、test2プロパティが存在している
その中にtest3プロパティが存在していて、かつ中の値も存在しているか確認

const test = {
  test2: {
    test3: true
  }
}

console.log(deep(test, 'test2.test3'))

出力結果

true

存在している場合はtrue、存在していない場合はfalseが返ってくる

delayメソッド

async/awaitを使用するときに、setTimeoutPromiseを返す

const delay = ms => {
  return new Promise(resolve => setTimeout(resolve, ms))
}

使い方

awaitdelayメソッドを使い、引数にはミリ秒単位で時間を入れる、その間その後の処理は行われない

(async function() {
  await delay(3000)
  console.log('3秒経ちました。')
}())

出力結果

3秒経ちました。

3秒後に「3秒経ちました。」と出力

参考文献

この記事は以下の情報を参考にして執筆しました。

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

emotion (emotion.js) のStyled Componentsで作成した要素にカスタム属性を渡したくなったら (shouldForwardProp)

今日ハマったのでメモ。

Reactはバージョン16から要素に好きに属性を生やせます

Reactはバージョン16からカスタム属性の利用を認めています。

たとえば、下記のような属性指定ですね。

<a mycustomattr="customvalue">foobar</a>

上記の場合だときちんと mycustomattr="customvalue" が実際のDOMにレンダリングされます。

もともとReactでは、HTML公認のカスタム属性である data-** や、幅広い語彙を持つ aria-** は利用を認められていたのですが、その他の自作の属性、あるいはHTML5でいなくなった古い属性(e.g. <td> 要素の align 属性)は、カスタム属性が使えないと表現できません。

emotionのStyled Componentsはそのままだとカスタム属性をはたき落とします

そして本題ですが、タイトルの通りemotionのStyled Componentsは、デフォルトでカスタム属性をはたき落とします。

たとえば、

const CustomElem = styled.div`
  color: red;
`

// @ts-ignore
return <CustomElem mycustomattr="customvalue">Hello</CustomElem>

(@ts-ignore はTypeScriptさんが mycustomattr なんてないよ!とエラーを出すのを抑制するために入れています。JSの人は要りません。)

上記のコードだと mycustomattr がはたき落とされます。基本的には、is-prop-validという実装に従って、HTMLに無さそうな属性は自動的に消してくれるようです。

今までのReactであれば、存在しない属性を渡すと警告を出していたので、特に自前propsが無意識に渡されて警告が出がちなStyled Componentsにおいては重要な機能と言えるでしょう。

emotionでカスタム属性を通す方法

ただ、当然このままだと困るケースもあるわけで、じゃあそういう場合はどうするかというと、ドキュメントに書いてあるとおり(←最初からちゃんとドキュメントを読めよ!)、 shouldForwardProp を指定して、自分のカスタム属性を通してあげましょう。

const CustomElem = styled("div", {
  // mycustomattr と children(子要素) のみ許可する
  shouldForwardProp: (props) => ["mycustomattr", "children"].includes(props)
})`
  color: red;
`

// @ts-ignore
return <CustomElem mycustomattr="customvalue">Hello</CustomElem>

ちゃんと結果のDOMに mycustomattr がいるはずです。逆に、他の属性を渡しても何もDOMに影響しないはずです。

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

【OnsenUI】画面遷移とアニメーションの設定方法

こんにちは。
OnsenUIの学習中につまずいた、画面遷移の方法をまとめてみます。

画面遷移

main.js
if (page.id === 'page1') {
  page.querySelector('#switch_button').onclick = function() {
    document.querySelector('#myNavigator').pushPage('page2.html',slide);
  };
} else if (page.id === 'page2') {
  page.querySelector('#return_button').onclick = function() {
    document.querySelector('#myNavigator').popPage(slide);
  };
};

画面遷移には、pushPage popPageメソッドを使用します。
引数にはあらかじめ作成したページと、アニメーションを指定します。(poppageにはアニメーションのみ)

アニメーションの設定

main.js
let slide = {
  animation: 'slide',onTransitionEnd: function(){}
};

animationには任意の値を指定できます。
OnsenUIで用意されているアニメーションは以下の4つ。

  • slide
  • lift
  • fade
  • none

参考文献

OnsenUIの基礎-OnsenUI

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