nayucolony

勉強したこととか

レスポンシブのサイト制作で、必要なカンプは?

結論はないですが最後にいちコーダーとしての個人的なわがままがあります。

事の発端

この発言。TLで「モバイルファーストでCSS書く」みたいな話を見かけて、そういえばないなあっていう思いから漏れ出たツイーツでした。

注意事項

まず、ツイッターで意見を募る系の世論調査、業界全体でみると「意識高い人(NOTただ意識高い系の人)」が意見をくださる場合が多いです。 なので、わりと「アンテナはってる」層の意見が多いパターンが多く、一般世論とは乖離している可能性が高いことをご留意ください。

また、「モバイル/SP」のような表現をしますが「小さい画面」という意味で言っており、特定のデバイスをさしているわけではないのでご了承ください。

あと、私は元デザイナーでありコーダーですが、互いの職域に対する文句やdisではありません。 いただいた意見を元にした現状の解釈と、個人的な意見です。

ツイートに対する反応

この辺は、僕のツイートが「つらみのぼやき」に見えてしまったのか、デザイナーの「そんなことないよ」という反応とコーダーの「だよなー」という反応が多くを占めました。

デザイナーの反応

  • まじで?そんなパターンあるの?
  • PCとSPを想定してつくる
  • 大中小の画面パターン用意する
  • 実装者が社内のウェブデザイナーとかならコミュニケーションで回す

etc.

コーダーの反応

  • あるある
  • やっぱどこもそんな感じなんだな
  • 「よしなに」の指示が来ることが多い。工数増えるっていう話をすると辞退される
  • 完全なカンプは予算によっては存在するが、ディレクターが手書きワイヤーフレームで指示くれてコーダーが実装する
  • 元デザイナーなのである程度汲み取る
  • SPはトップだけくる

etc.

デザインカンプの持つ側面

たくさんの意見をいただいて、次のような側面が存在することに気づきました。

  • デザインカンプは、次工程への指示書である
  • デザインカンプは、前工程への報告書である

前者はコーダーに渡すカンプとして、後者はクライアントに見せるカンプとしてという意味合いです。

コーダーは「別バージョンのカンプ」が欲しいのか

そもそも、欲しいのか、欲しくないのか、ということから考えました。 結論からいうと、場合によるよね、とはなるのですが(笑)

前職では、クライアントの性質上「PCからの流入が圧倒的」であり、「小さい画面」については存在さえしていればいい、本当によしなにやってくれればいい、という感じでした。 しかし、「じゃあ適当にやっときますわ」というわけにもいかないのが現実でした。

なぜなら、「PC向けのデザインが本当にPCの画面いっぱいを使って表現するようなデザイン」であり、段組を変えてレイアウトを変更すればいいという話ではすまなかったからでした。

webコミックの話

具体的なサイト名を出せないので、下手くそなたとえ話をします。

例えば、スマートフォンでマンガを読む場合は、見開きではなく1ぺージずつスワイプを行なって読むことが多いですよね。もしくは、縦スクロール。 その際「見開きをぶち抜いて表現しているページ」が本来作者の意図した見せ方ができなくなりますよね。

もう、web連載なんかでは縦スクロールを前提としたコマ割りを行われているケースが多いですが、もし紙の本にむけて書いたものをその形式で出してしまうとほんとうに悲惨なことになります。 (その場合、そのコマにさしかかたときだけ横にスクロールするマンガもありますが)

・・・とまあ、話がそれましたが、その辺に近いものがありました。

そのため、「まあよしなにやってくれや」というオーダーがきた場合、かなり無理があるので、狭い画面向けのデザインの再構築をお願いしたいという気持ちがあるというのが当時の状況でした。

デザイナーは「別バージョンのカンプ」を作りたいのか

まず、多かった意見が「クライアントのビジュアルチェックがあるので絶対に必要になる」というパターンでした。これは私もやっていました。

特にコンペとかだと、おそらく100%のデザインを出すんでしょうし、それがそのままコーダーに渡って来る可能性が高いです。 レギュレーションとかもありますし、前職ではプレスチェックのために完璧なデザインを提出していました。

そのフェーズで「クライアントのビジュアル的要求」と「文書構造の一貫性」を両立することが困難になるケースも多くて、カンプのデザイン以外は「誰も知らない中間の姿」となってしまうこともありました。 当時はとにかくデザインのOKをもらうことを優先していたため、いつも、そこの調整を闇雲に行っては消耗していました。

その辺を確実に実装させるための戦略として、別バージョンのカンプを用意すると言う意見もありました。 それだけの予算をとる説得ができるかどうかは、もう一つ上の工程に掛かっているのかとはおもいますが、納期と予算の都合で難しいケースもあるとのことです。

コーポレートデザイン的な部分も含めてサイトを作ることのウェイトが高い場合は、そういう予算も惜しまずだしてもらえるのでしょうか。

ワイヤーフレームをしっかり決めることが大切

たじまさん(@DesignHumore) という、ブランディング戦略から現場のデザイナーまでを担当されている方がいて、その方の意見がすごく参考になりました。

複数ツイートに渡っての会話だったので、要約すると以下のような形になります。

  • ワイヤーの時点で仕様を徹底的に詰める。必要な要素に抜け漏れがないかの確認が目的。
  • デザイン(ビジュアル的な話)はイメージボードを使って方向性を決める
  • 「とはいえ実物見ないとわからない」場合も当然あるが、その場合は30%くらいの力でデザインしてイメージを見せる。
    • コーダーへの指示書ではなく、お客さんへのイメージ共有なので、最適レベルで見せるという意味だと思う。
  • デザイナーとしては、装飾の変更は言うほどつらくない。情報構造の変化は、大変。

デザイナーはデザインツール上で完結することはテクニックで完了させられますが、インフォメーションアーキテクト的な話は、そこだけでは完結させられないのでクライアントを巻き込んだ手戻りが発生しがちです。

実装でもそうで、CSSの書き換えはSassなんかを駆使すればけっこう簡単に終わらせられます。 しかし、情報構造が変わると、HTMLも含め考慮することが増えがちです。サーバサイドと連携している場合はなおのことです。

たじまさんは「その手戻り・修正にかかる費用と時間はお客さんのものだから勝手に資源にしてはならない」とも仰っていました。

作業の大変さとかしか考えてなかったので感心しました。

文書構造の変更は大変だが、あしらいの調整はそうでもない

たじまさんの話をうけて、下手くそなたとえ話をします。

家を建てる過程で、もう色々な作業が動いているときに「すみません!忘れてたんですけど、2Fにもトイレが必要なんでした!このスペースに置いてくれればいいんで!」みたいな話をされた時の話です(なんだそれ)。

  • 「いやいや、スペースはあるけど、このスペースは居住者の使いやすさをいろんな面から考慮してデザインしてまして・・・」
  • 「水道管の整備、電源の確保、換気ダクトの設置、収納、あらゆる要素を改めて考慮するので、一旦全作業をストップしてもらって、もう一度打ち合わせが必要です」
  • 「モノがありません、完了予定日は決まっています、無理です」

みたいな問題が生まれると思うのですが、コーディングもなんか、似たようなことが起こります。 なるべく文書構造のぶっこわしは発生してほしくないと思っています。

しかし、「すみません、外装なんですが、やっぱりクライアントのイメージと少し違っていて、もう少し濃い茶色のモノに変更してください!」という場合。

  • 「塗装業者をもう一回呼びましょう。最悪、材変えましょう。お金はかかりますが、いけますよ」
  • 「壁紙剥がして張り替えですかねー、内装業者のスケジュールにもよりますが、無理はないですよ、そのぶんお金はかかりますけど」
  • 「この色は、全体の調和を考えてデザインしていて、他の部分もトーンを変えないと変な感じになっちゃいそうですが、本当に必要ですか?」

みたいな、割とお金で解決できるフェーズじゃないかとおもいます。 この辺は、CSSを書くときにだいたい想定しているので、割と変更は楽です。

web制作の現場、家とかに例えると「なんだそれ」ってなりますが、結構頻繁にこういう事が起こっているのを考えるとおかしな部分がありますね。 リアルの物体じゃないという性質上、修正が簡単なのは、わからなくもないんですが。

私なりのわがまま

最後に、個人的な意見です。

大きな画面でないと表現できないようなデザインならば、画面が小さくなった時のデザインも欲しい

例えば、横長のヒーローイメージをドカンと置いているような場合、「よしなに」と指示をすると「画面幅に合わせて縦横比を合わせて縮小」されて、すごくダイナミックな表現だったはずのものがすごくみすぼらしくなったりします。 デザイナーとしては「私はこんなの認めないぞ!これはこれに向けたデザインをしなおす!」という気持ちになるのではないでしょうか? その辺りを放棄されると実装側は萎えるということもあり、お互いいい仕事をするためにも、ビジュアルの共有をしてほしいです。

レイアウトの変更だけでいけるようなサイトならば、ワイヤーレベルの指示書が欲しい

複数のカラムで構成されていて、1カラムに並べ替えるとか、1カラムを表示させないとか、そういう操作だけで行けるような場合はカンプはそこまで重要ではないかなとおもいます。 逆に、ケツの姿が決まってしまっていて、クライアントにも「これでいきます!」と固めてしまうと、中間の処理なども含めて無理が発生することが多いです。

・・・まあ、クライアントのチェックが入る以上、必要なものは必要なので、仕方ないのですが。

ただ、要素の増減が発生する場合、何を削ったらいいのかとかは実装者の独断では決められないので、指示書レベルで欲しいという気持ちがあります。 手書きワイヤーでもいいです。デザインも、デザインをかじったレベルでいいのならばまかせてくれという感じです。

結局、コミュニケーション

中間成果物がなくても、コミュニケーションで巻き取れる部分はかなりあると思っています。

  • 「ふってきたデザインがこれで」
  • 「ふってきたオーダーがこれで」

という風に何か超常現象のように捉えてしまいがちですが(いや、あいつぶっ殺すという人もいるとは思いますよ)、やはり決めてるのは人なのでコミュニケーションを大事にしたいですね。

そのコミュニケーションのためのベースとして、実装者はデザインに対する理解を深めておきたいし、デザイナーは実装に対する理解を深めていけるといいなと思っています。

最後に

元も子もないような感じではありますが・・・

「手戻りが発生することも含めて、お金で解決するのがプロフェッショナル」

ということもあるということを忘れてはならないと思います。

とはいえ、湯水のようにお金を使う事がプロフェッショナルかというと、そうではないということも合わせて肝に命じておきたいですね。

最後まで読んでいただきありがとうございました。

touchstartをイベントハンドラとした時のpageX,pageYプロパティへのアクセス方法

イベントハンドラmousedownのときとtouchstartの時で引数に渡されるオブジェクトが異なるというメモ。

mousedownのときに渡されるオブジェクト

MouseEventオブジェクトが渡される。

目的のpageXpageYプロパティは直下にあるので

e.pageX
e.pageY

でアクセスできる。

MouseEvent - Web API インターフェイス | MDN

touchstartのときに渡されるオブジェクト

TouchEventオブジェクトが渡される。

TouchEvent - Web API インターフェイス | MDN

場合によってはMouseEventも渡されるっぽいが、あらゆる場合で渡されるのかは未調査。

MouseEventオブジェクトとは違い、pageXpageYプロパティは直下には存在しない。 ではどこにあるのかというと、TouchEventオブジェクトのchangedTouchesプロパティの中だ。

TouchEvent.changedTouches - Web API インターフェイス | MDN

ここにtouchListというオブジェクトが格納されている。

TouchList - Web API インターフェイス | MDN

touchListオブジェクトの中には0というプロパティ名でTouchオブジェクトが格納されている。 この中にpageXpageYが入っている。

Touch - Web API インターフェイス | MDN

そのため、アクセスするには以下のように記述すればOK。

e.changedTouches[0].pageX
e.changedTouches[0].pageY

オブジェクトのプロパティへアクセスする際の注意事項

JavaSctiptでは、ブラケット表記法でオブジェクトのプロパティにアクセスする際に数値をいれても文字列として扱われる。 ブラケット表記法には式の評価を行うプロセスがあり、その際にtoStringメソッドを経由するので、強制的に文字列型への変換がかかるためである。

メンバー演算子 - JavaScript | MDN

一方、ドット表記法はそのプロセスは存在しない。 したがってe.changedTouches.0のような表記は使えないので注意。

これらについては別記事でまとめてあるのできになる場合はそちらで確認してほしい。

変数をキーとしてオブジェクトのプロパティを参照する際はブラケット表記法を使う。 - nayucolony

関連

今回のエントリの内容は以下のサンプルコードに登場します。

弾力のあるヘッダ - Vue.js JSFiddle

SVGを触ってみる【path要素/d属性/直線/ベジェ曲線】

SVGのことを実は全く知らないので少し調べてみた。

とりあえず、簡単なパスを引いて図形をつくる。

まず、基本事項としてsvg要素が存在し、子要素としてpath要素を作る。 そしてpath要素にd属性を指定することでパスを引いていく。ddraw = 線を引くという意味。

パスの内側を塗りつぶす時はfill属性を使用する。

直線を引く場合

始点を決めたら、あとは座標をどかどかうっていくと直線をどんどん引いていける。

Mで始点の座標を決定し、L以降に指定した座標に直線を引いていく。LはLineto〜に直線を引く 以下の例では、0,0を支店に、順番に320,0=>320,160=>0,160と線を引いた場合。

0,160 => 0,0のように始点までの直線を指定しなくても、最後の座標から自動的に始点にまでのパスを引いてクローズドパスになる。

<svg width="1000" height="1000">
  <path d="M0,0 L320,0 320,160 0,160" fill="#3F51B5"></path>
</svg>

f:id:nayucolony:20170604040750p:plain

座標は書いた順番にパスが惹かれていくので、入れ替えると見た目は変わる。

<svg width="1000" height="1000">
  <path d="M0,0 L320,160 320,0 0,160" fill="#3F51B5"></path>
</svg>

f:id:nayucolony:20170604040636p:plain

曲線を引く場合

曲線を引く場合はQを使う。 曲線はベジ絵曲線で、クアドラティックベジエとキュービックベジエの二種類がある。Qだと「クアドラティックベジエ」が惹かれる。 名前が違えば当然処理も違うが、詳しいことはまだ調べきれていない。

以下は、一番最初の図形を少し変更したもので

320,160 => 160,320160,320 => 0,160が曲線になる。

<svg width="1000" height="1000">
  <path d="M0,0 L320,0 320,160 Q160,320 0,160" fill="#3F51B5"></path>
</svg>

f:id:nayucolony:20170604041952p:plain

注意

この図形描画は、svg要素がその直線を引くのに必要なだけの領域を確保できていることが前提となる。 上記までのサンプルは、しれっと1000px * 1000pxを確保していたが

たとえば2つ目のサンプルは、描画に320px * 160pxを必要とする。 ここで、svg要素の領域を160px * 80px にしてみる。

<svg width="160" height="80">
  <path d="M0,0 L320,160 320,0 0,160" fill="#3F51B5"></path>
</svg>

すると、こうなる。

f:id:nayucolony:20170604042736p:plain

path要素はちゃんと描画領域を確保しているものの、svgが領域を確保していないためにクロップされている、と表現するのが正しそう。

参考

d - SVG | MDN

インスタンス内において、アロー関数の「this」はインスタンスを参照しない

Vue.jsに限った話ではないが、Vue.jsを触っていて湧いたちょっとした疑問を調べたメモ。

TL;DR

  • アロー関数内のthisは「Vueインスタンスを参照したい」という文脈での使用に適さないのでつかわない。
  • functionによる関数宣言では「インスタンス内のthisインスタンスそのもの」なのでVueインスタンスを参照できる。
  • アロー関数では「呼ばれた場所がthis」。
  • 例えばcomputedのメソッド内のthisは「computedのオブジェクト」であり、インスタンスではないので不適。

経緯

Vue.jsに書いてある

算出プロパティ(例 aDouble: () => this.a * 2) を定義するためにアロー関数を使用すべきではないことに注意してください。アロー関数は、this が期待する Vue インスタンスではなく、this.a が undefined になるため、親コンテキストに束縛できないことが理由です。

とか

data プロパティ(例 data: () => { return { a: this.myProp }}) でアロー関数を使用すべきではないことに注意してください。アロー関数は、this が期待する Vue インスタンスではなく、this.myProp が undefined になるため、親コンテキストに束縛できないことが理由です。

これが気になってちゃんと調べた。

解説

Vue.jsを使用する際、「Vueインスタンスdataオプションのオブジェクトに入ってる値」を参照して計算することが多々ある。 例えばこんな感じ(Vue.jsより)

var vm = new Vue({
  data: { a: 1 },
  computed: {
    // get のみ。必要なのは関数一つだけ
    aDouble: function () {
      return this.a * 2
    },
    // get と set 両方
    aPlus: {
      get: function () {
        return this.a + 1
      },
      set: function (v) {
        this.a = v - 1
      }
    }
  }
})
vm.aPlus   // -> 2
vm.aPlus = 3
vm.a       // -> 2
vm.aDouble // -> 4

このとき、this.aは「dataオプションのオブジェクトのプロパティ名a」をさしている。

これは、「コンストラクタを呼び出してインスタンスを生成したとき、インスタンス中のthisインスタンスそのものをさす」ことによる。 datamethodなどのオプションのオブジェクトのプロパティは、Vueインスタンス直下に格納されるので、this.aのようにして参照できる。

ちなみに、所属するオプションを明示的にしてアクセスをする場合、this._data.aだったりthis.$data.aだったりでアクセスできる。 (ただし、アクセスできるものの、「完全に代替できる」とはいえないっぽいということがオプション / データ - Vue.jsに書いてある。 使用する際は一読することを推奨する。)

では、アロー関数でのthisはどうなのか。 アロー関数のthisは、functionの時のように「インスタンス内ではそのインスタンスをさす」という動きはしない。

以下は先ほどの例をただアロー関数に変えたもの。

var vm = new Vue({
  data: { a: 1 },
  computed: {
    // get のみ。必要なのは関数一つだけ
    aDouble: () => {
      return this.a * 2
    },
    // get と set 両方
    aPlus: {
      get: () => {
        return this.a + 1
      },
      set: (v) => {
        this.a = v - 1
      }
    }
  }
})

ここで、一部分に着目

var vm = new Vue({
  data: { a: 1 },
  computed: {

    aDouble: () => {
      return this.a * 2
    },

  }
})

このとき、funcitonではthis.adataオプションのaを参照できていた。

しかし、アロー関数を用いるとそうはできない。なぜなら、アロー関数は「呼び出された場所をthisとする」という動きをするからだ。 「呼び出された場所」は今回のケースだと、「computedオプションのオブジェクト」にあたる。 そのオブジェクトの中にaをプロパティ名として持つ要素は存在しないので、undefinedとなる。

これが、

アロー関数は、this が期待する Vue インスタンスではなく

という文章の意味だった。たしかにVueインスタンスを参照できていないので不適だ。 this使ってない場合はアロー関数でも問題はないが、混合させるくらいならfunctionで統一すべきだろう。

関連

アロー関数がどうのこうのと言う前に、そもそもthisは結構文脈によってさすものが違うので注意。 JavaScriptの「this」は「4種類」?? - Qiita

変数をキーとしてオブジェクトのプロパティを参照する際はブラケット表記法を使う。

結論

  • JavaScriptにおいて、変数をキーとしてオブジェクトのプロパティを参照する際はブラケット表記法を使う。
  • ブラケット表記法には、ドット表記法にはない「式の評価」処理があるため、変数を解決できる。

経緯

for文の中にfor文を配置して二次元配列的にループしてさせようとしていた。

<div id="demo">
  <tr v-for="entry in gridData">
    <td v-for="key in gridColumns">{{ entry.key }}</td>
  </tr>
</div>
const demo = new Vue({
  el: '#demo',
  data: {
    gridColumns: ['name', 'power'],
    gridData: [
      { name: 'Chuck Norris', power: Infinity },
      { name: 'Bruce Lee', power: 9000 },
      { name: 'Jackie Chan', power: 7000 },
      { name: 'Jet Li', power: 8000 }
    ],
  },
});

するとこうなった。

f:id:nayucolony:20170530191723p:plain

ちゃんとループはしているのだが、データがバインドできていないという状態だ。

解決方法

オブジェクトのデータ参照方法をドット表記法でなくブラケット表記法に変更した。

それぞれの表記法については下記の通り。 メンバー演算子 - JavaScript | MDN

<div id="demo">
  <tr v-for="entry in gridData">
    <td v-for="key in gridColumns">{{ entry[key] }}</td>
  </tr>
</div>

f:id:nayucolony:20170530192017p:plain

チャックノリスもにっこり。

なぜなのか

ブラケット表記法とドット表記法は、単純に「ふた通りの表記法があります」という話ではない。 たとえば、今回のケースではkeyが変数であるということが大きなポイント。ドット表記法では、プロパティ名に変数をもちいることができない

ここに書いてあった。 ECMAScript 2015 Language Specification – ECMA-262 6th Edition

12.3.2.1 Runtime Semantics: Evaluation

MemberExpression : MemberExpression [ Expression ]
1.Let baseReference be the result of evaluating MemberExpression.
2.Let baseValue be GetValue(baseReference).
3.ReturnIfAbrupt(baseValue).
4.Let propertyNameReference be the result of evaluating Expression.
5.Let propertyNameValue be GetValue(propertyNameReference).
6.ReturnIfAbrupt(propertyNameValue).
7.Let bv be RequireObjectCoercible(baseValue).
8.ReturnIfAbrupt(bv).
9.Let propertyKey be ToPropertyKey(propertyNameValue).
10.ReturnIfAbrupt(propertyKey).
11.If the code matched by the syntactic production that is being evaluated is strict mode code, let strict be true, else let strict be false.
12.Return a value of type Reference whose base value is bv and whose referenced name is propertyKey, and whose strict reference flag is strict.

MemberExpression : MemberExpression . IdentifierName
1.Let baseReference be the result of evaluating MemberExpression.
2.Let baseValue be GetValue(baseReference).
3.ReturnIfAbrupt(baseValue).
4.Let bv be RequireObjectCoercible(baseValue).
5.ReturnIfAbrupt(bv).
6.Let propertyNameString be StringValue of IdentifierName
7.If the code matched by the syntactic production that is being evaluated is strict mode code, let strict be true, else let strict be false.
8.Return a value of type Reference whose base value is bv and whose referenced name is propertyNameString, and whose strict reference flag is strict.

どうみてもプロセスの数が違う。ブラケット表記法の方がプロセスが多い。 私の解釈が間違っていなければ、「式の評価」というプロセスがあるかないかという差分がある。 (そもそも、MemberExpression [ Expression ]MemberExpression . IdentifierNameという違いがある。前者は式、後者は識別子と明確に別物)

ブラケット表記法では、変数を解決するプロセスをしっかりもっているが、ドット表記法にはそれがない。

ということで、これはべつにバグではなくかっちり定義されている仕様。二つの表記法は好みで使い分けていいものだと思っていたのでいい勉強になった。

関連

今回の現象は以下のサンプルをいじっていたときに発生したものでした。

グリッドコンポーネント - Vue.js