Skip to content

Instantly share code, notes, and snippets.

@xl1
Created December 6, 2012 23:54
Show Gist options
  • Save xl1/4229530 to your computer and use it in GitHub Desktop.
Save xl1/4229530 to your computer and use it in GitHub Desktop.

Revisions

  1. xl1 revised this gist Dec 7, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -29,7 +29,7 @@ WebKit では、`resize` を指定した要素は「大きく」することは
    左上方向にはドラッグができないことになります。
    これを解決するには、最初は `margin``left`, `top` などで初期位置を指定し、少し移動された時点で(`style` 属性がついた時点で)それらを解除する、ということが必要です。

    - [Demo 1. ドラッグできる要素](http://jsdo.it/xl1/wV5c/fullscreen)
    - [Demo 1. ドラッグできる要素](http://jsdo.it/xl1/wV5C/fullscreen)

    ## resizer をデザインする

  2. xl1 revised this gist Dec 7, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@

    CSS プログラミングの基本は、「ユーザの操作をなんとかして受け取ってスタイルをあてる」ということの繰り返しです。
    クリックの検出に `:checked``:active`, mouseover なら `:hover` を使う……とやるわけですね。
    ならば次は「__ドラッグ__」も検出したいところです。
    ならば次は「ドラッグ」も検出したいところです。

    ## それ CSS でできるよ!

  3. xl1 revised this gist Dec 7, 2012. 1 changed file with 119 additions and 1 deletion.
    120 changes: 119 additions & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -1 +1,119 @@
    # 建設予定地
    # CSS でドラッグをきめる

    これは [CSS Programming Advent Calendar 2012](http://www.adventar.org/calendars/2) の 7 日目の記事です。

    CSS プログラミングの基本は、「ユーザの操作をなんとかして受け取ってスタイルをあてる」ということの繰り返しです。
    クリックの検出に `:checked``:active`, mouseover なら `:hover` を使う……とやるわけですね。
    ならば次は「__ドラッグ__」も検出したいところです。

    ## それ CSS でできるよ!

    ドラッグで操作できる HTML 要素というと、まず `input[type=range]` が思い浮かびます。
    しかし残念ながら、`input` 要素の `value` の値は動的に変化しない(CSS で検知できない)うえ、一方向にしか動かせず自由度が低いです。
    そこで、`resize` プロパティを使います。
    `textarea` 要素のシステムスタイルに設定されているアレです。

    ![resizer に注目](http://jsrun.it/assets/a/z/p/5/azp5n.png)

    ```css
    resize: both; /* または horizontal, vertical */
    overflow: auto; /* visible 以外 */
    ```

    上のようにした要素はリサイズが可能になりますが、この「取っ手(resizer)」の部分を利用するわけです(ただし IE や Opera では使えないようです)。
    適当なスタイルをあてれば、リサイズしているのではなくあたかも「ドラッグして移動している」ように見せることができます。

    ## 初期位置を自由に決める

    WebKit では、`resize` を指定した要素は「大きく」することはできますが、もともとの `width`, `height` より小さくリサイズすることはできません(Firefox なら可能)。
    左上方向にはドラッグができないことになります。
    これを解決するには、最初は `margin``left`, `top` などで初期位置を指定し、少し移動された時点で(`style` 属性がついた時点で)それらを解除する、ということが必要です。

    - [Demo 1. ドラッグできる要素](http://jsdo.it/xl1/wV5c/fullscreen)

    ## resizer をデザインする

    このままでは大きさやデザインが固定で扱いづらいですが、WebKit では `::-webkit-resizer`, `::-webkit-scrollbar` 擬似要素を利用してスタイルを指定することができます。
    上の初期位置指定とまとめて、次のような SCSS mixin を用意すると便利です。

    ```scss
    @mixin draggable-frame($width, $height, $left, $top, $style: both) {
    resize: $style;
    overflow: hidden;
    width: $left + $width;
    height: $top + $height;

    // 以下 WebKit の場合
    @media screen and (-webkit-min-device-pixel-ratio: 0) {
    overflow: scroll; // ::scrollbar を必ず存在させておく
    width: $width;
    height: $height;
    // 最初は margin を指定しておく
    margin-left: $left;
    margin-top: $top;

    &:active {
    // 移動されようとしている(:active)(かつ、まだ移動されていない)状態で
    // 少しだけ左上にずらすと、左上への移動もうまくいく(ことが多い)
    margin-left: $left - 1;
    margin-top: $top - 1;
    }
    &[style] {
    // 少しでも移動されたら margin をゼロに
    margin-left: 0;
    margin-top: 0;
    }
    }
    &::-webkit-scrollbar {
    // ::scrollbar のサイズを指定すると ::resizer のサイズが変わる
    // ::resizer に直接 width, height を指定しても効果なし
    width: $width;
    height: $height;
    }
    &::-webkit-resizer, &::-webkit-scrollbar-corner {
    visibility: hidden;
    }
    }
    ```

    スクロールバーまわりの擬似要素についてはこちらの記事が網羅的でわかりやすいです。

    - [スクロールバーをデザインする ::-webkit-scrollbar に関しての覚え書き | Unformed Building](http://unformedbuilding.com/articles/learn-about-webkit-scrollbar/)

    また、CSS Transforms を利用してスタイリングをすることもできますが、それでも Firefox では表示と resizer の実際の位置がずれてしまい、意図した通りに動かすことができませんでした。
    Firefox で resizer の大きさを変える方法があれば教えてください。

    - [Demo 2. ドラッグできるダイアログボックス](http://jsdo.it/xl1/fUjx/fullscreen)

    ## ドラッグ位置を取得する

    先に述べたように、`input` 要素の `value` は動的に変化しないのが難点でしたが、`resize` を指定された要素はインラインスタイルの形で `width`, `height` が変化してくれます。
    属性セレクタで `[style*="width: 300px"]` などとルールを指定すれば、「このくらいドラッグされたらスタイルを当てる」というようなことができるのです。
    次のデモでは `[style*="width: 3"]` などとして、最上位桁の値によって 100px ごとに分岐させています。

    - [Demo 3. ドラッグでスライドを切り替える](http://jsdo.it/xl1/8SWP/fullscreen)

    インラインフレームの大きさを変え、`background-image` や Media Queries を利用して描画に反映させる方法もあります。
    別のドキュメントになってしまいますが、閾値を細かく指定しやすい(上の Demo 3 は 100px 固定でしたね)利点があります。

    - [Demo 4. インラインフレームを利用する](http://jsdo.it/xl1/d76E/fullscreen)

    (ドラッグと関係ないですが) Media Queries で状態を保持するテクニック自体がかなり便利で、例えば次のものに使われていました。

    - [Media Query Mario | Demo Studio | MDN](https://developer.mozilla.org/ja/demos/detail/media-query-mario)
    - [Pure CSS3 calculator](http://experiments.hertzen.com/css3calculator/)

    [CSS Custom Filters (CSS Shaders)](http://html.adobe.com/webstandards/csscustomfilters/) を利用してドラッグを受け取る手もあります。
    Custom Filters とは、CSS で OpenGL ES 2.0 / WebGL 互換のシェーダエフェクトをかけられるようにするものです。
    [WebKit Nightly](http://nightly.webkit.org/)[Chrome dev](http://www.chromium.org/getting-involved/dev-channel) には実装が載っています(設定で有効にする必要があります)。
    このシェーダに表示領域のサイズを `u_textureSize` として渡してやり、シェーダ側で処理を行なって最終的な表示に反映するわけです。
    しかしこれを CSS プログラミングと呼んでいいのか疑問であります……。

    - [Demo 5. Custom Filter を利用する](http://jsdo.it/xl1/rD74/fullscreen)

    というわけで、ただし WebKit に限る、という部分もありましたが、なんとかドラッグに反応することができました。
    めでたし、めでたし……

    ## 8 日目は

    明日は [GeckoTang](http://geckotang.tumblr.com) さん(7 日ぶり 2 度目)です!
  4. xl1 created this gist Dec 6, 2012.
    1 change: 1 addition & 0 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    # 建設予定地