tag:crieit.net,2005:https://crieit.net/tags/3D/feed 「3D」の記事 - Crieit Crieitでタグ「3D」に投稿された最近の記事 2018-12-20T09:04:16+09:00 https://crieit.net/tags/3D/feed tag:crieit.net,2005:PublicArticle/14482 2018-07-17T06:20:14+09:00 2018-12-20T09:04:16+09:00 https://crieit.net/posts/Vue-js-HTML-CSS-3D Vue.jsとHTMLとCSSで3Dアクアリウムを作ってみた <p>ブラウザで見ることができる3Dアクアリウムを作ってみました。</p> <p>この3Dの実装は基本的にはHTMLとCSSだけで出来ています。canvasも使っていません。Vue.jsも使っていますが、魚を泳がせたり視点を変更するための用途のため、3D描画自体にはあまり関係ありません。</p> <p><a href="https://crieit.now.sh/upload_images/0dd6dfb5be148e18cf69c9c184c4568e5b48b41c0a3fe.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/0dd6dfb5be148e18cf69c9c184c4568e5b48b41c0a3fe.png?mw=700" alt="Screenshot from 2018-07-13 23-14-23.png" /></a></p> <p>記事の最後に操作できるCodePenを配置してあるので読むのが面倒な方はそちらを見てしまってください(大泣きしながら)。</p> <h2 id="作り方"><a href="#%E4%BD%9C%E3%82%8A%E6%96%B9">作り方</a></h2> <h3 id="HTML"><a href="#HTML">HTML</a></h3> <p>HTMLは単にそれぞれの素材を配置しているだけです。</p> <pre><code class="html"><div id="app" @mousemove="onMouseMoved" @touchmove="onMouseMoved"> <div class="container" :style="rotation"> <img class="bg" src="bg.jpg"> <img class="water front" src="water.jpg"> <img class="water side left" src="water.jpg"> <img class="water side right" src="water.jpg"> <img class="water top" src="water.jpg"> <img class="ground" src="ground.jpg"> <img v-for="fish in fishes" class="fish" :style="fishStyle(fish)" :src="fish.image"> </div> </div> </code></pre> <p>見ての通り上記のようにfishesに魚のデータが入っていてそれを描画しているだけです。fishesは下記のようにVueコンポーネントのステートとして持たせてあります。</p> <pre><code class="javascript">new Vue({ el: "#app", data() { return { rotationX: 0.0, rotationY: 0.0, fishes: [this.generateFish(), this.generateFish()] } }, </code></pre> <p>generateFishは下記のように魚のデータをランダムで初期化しています。</p> <pre><code class="javascript"> generateFish() { return { image: fishImages[Math.floor(Math.random() * fishImages.length)], x: Math.floor(50 + Math.random() * 200), y: -50 + Math.floor(Math.random() * 100), z: -100 + Math.floor(Math.random() * 200), ax: Math.floor(Math.random() * 2) == 0 ? -1 : 1 } }, </code></pre> <h3 id="CSS"><a href="#CSS">CSS</a></h3> <p>CSSで3Dにしたい部分をまずこれで囲んでいます。</p> <pre><code class="css">.container { position: relative; margin-left: auto; margin-right: auto; transform-origin: 50%; transform-style: preserve-3d; display: flex; align-items: center; justify-content: center; width: 400px; height: 100%; } </code></pre> <p>重要なのは下記です。</p> <h4 id="transform-style"><a href="#transform-style">transform-style</a></h4> <p>ゲームも同じですが、3Dには2つの描画パターンがあります。それは、奥行きを再現するモードと再現しないモードです。<code>preserve-3d</code>は再現するモードになるので、遠いものが小さく見えるようになります。</p> <h4 id="transform-origin"><a href="#transform-origin">transform-origin</a></h4> <p>これは各要素を回転する際に、どこを支点とするか、を指定するものです。今回、マウス操作で視点を変更できるようにしているので画面の中央、つまり50%のところを指定しています。</p> <p>何を言ってるかさっぱりわからない! という方は下記の説明をみると一目瞭然だと思います。<br /> <a target="_blank" rel="nofollow noopener" href="https://developer.mozilla.org/ja/docs/Web/CSS/transform-origin">transform-origin - CSS: カスケーディングスタイルシート | MDN</a></p> <h4 id="背景"><a href="#%E8%83%8C%E6%99%AF">背景</a></h4> <p>背景のCSSはこんな感じです。</p> <pre><code class="css">.bg { position: absolute; width: 400px; transform: translateZ(-200px); } </code></pre> <p><code>transform</code>の<code>translateZ</code>を使うことで、画面の奥に配置しています。左の水の壁などは下記のようになります。</p> <pre><code class="css">.wate-rleft { position: absolute; opacity: 0.3; left: -200px; transform: rotateY(90deg); } </code></pre> <p><code>left</code>で左に200pxずらし、<code>transform</code>の<code>rotateY</code>で90度回転させることでうまいこと前後の壁とくっつけて箱型になるようにしています。</p> <h3 id="お魚さん"><a href="#%E3%81%8A%E9%AD%9A%E3%81%95%E3%82%93">お魚さん</a></h3> <p>魚の位置はタイマーで下記のメソッドを呼び出して位置を動かしてあげます。</p> <pre><code class="javascript"> moveFish(fish) { if (fish.ax < 0) { if (fish.x <= 30) { fish.ax = -fish.ax; } } else { if (fish.x >= 300) { fish.ax = -fish.ax; } } fish.x += fish.ax; } </code></pre> <p>あとはこれをstyle属性にしてあげるだけです。</p> <pre><code class="javascript"> fishStory(fish) { const flip = fish.ax < 0 ? '1' : '-1'; return { left: `${fish.x}px`, transform: `scaleX(${flip}) translateY(${fish.y}px) translateZ(${fish.z}px)` }; }, </code></pre> <p>右に移動する時は画像を反転させるため<code>scaleX(-1)</code>、あとはX, Y, Z座標それぞれに配置するだけです。描画はVueが勝手にやってくれます。</p> <p>ほんとはCSSのanimationでゆらゆらと上下に揺らせていたのですが、どうもtransformの動作を壊してしまうようなのでやむなく削除しました。揺らすなら自分でそこも計算してyにプラスしてあげないといけないのかもしれませんね。</p> <h3 id="お魚さんの追加"><a href="#%E3%81%8A%E9%AD%9A%E3%81%95%E3%82%93%E3%81%AE%E8%BF%BD%E5%8A%A0">お魚さんの追加</a></h3> <p>クリックで魚を追加できるようにしておきました。いらすとやさんの魚で、何色かあったのでランダムな色で養殖します。</p> <pre><code class="html"> <div class="footer"> <button class="btn btn-default" @click="fishes.push(generateFish())">お魚さん生成</button> </div> </code></pre> <h2 id="成果物"><a href="#%E6%88%90%E6%9E%9C%E7%89%A9">成果物</a></h2> <p>下記が出来上がったVue+HTML+CSSの3Dアクアリウムです。</p> <p>マウスを動かすと視点が変わります。スマホの場合はタップすると一応mousemoveイベントが発生して視点を切り替えられるようです。スワイプでの操作は面倒だったので入れていません。</p> <p data-height="525" data-theme-id="0" data-slug-hash="GBJEOz" data-default-tab="result" data-user="dala00" data-embed-version="2" data-pen-title="3DAquarium" class="codepen">See the Pen <a target="_blank" rel="nofollow noopener" href="https://codepen.io/dala00/pen/GBJEOz/">3DAquarium</a> by dala00 (<a target="_blank" rel="nofollow noopener" href="https://codepen.io/dala00">@dala00</a>) on <a target="_blank" rel="nofollow noopener" href="https://codepen.io">CodePen</a>.</p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>canvasも使わずHTMLとCSSだけで3Dができるとは便利な時代になったもので驚きです。3Dモデルが使えないため利用用途としては限定的になりますが、それでも2D素材だけで十分なアプリケーションであればVueだけでとても簡単にできそうです。CodePenで試していましたが、これくらいの描画であればスマホでも対して重かったりということもなさそうでした。</p> <p>ただ、画像のシェアも出来ないので素直にcanvasで描画した方が使い勝手は良いかもしれませんね。何かよい案があれば是非使ってみてください。</p> だら@Crieit開発者