第6回: 正方形の代わりに画像を表示

2012/03/25

前回はCoffeeScriptでAjaxのコードを書く話をしました。

今回は趣向を変えて、読者の方からのメールにお答えしようと思います。以下、メールからの抜粋です。

CoffeeScriptについては勉強しようと思っていたところへ
CoffeeScript入門が掲載され、大変タイムリーでした。
今後の展開が楽しみです。

ところで、本記事では、blockを描画していますが、これを
png等のイメーファイルに置き換えてみようと思い、
ネットで調べたり、自分で推測してプログラムを変更してみましたが、
どうもうまくいきません。

いい質問です:)。ちょっとやってみましょうか。

その前に

いまdiv要素を追加しているところをimg要素に書き換えればよさそうですね。でもその前に、現行のソースコードを少し手直ししておきましょう。いまはこうなっています:

$ ->
  $("div#canvas").dblclick (e) ->
    [x, y] = positionOfNewBlock(e)
    $.post '/blocks', block: { x: x, y: y }, (block_id) ->
      block = $("<div class='block' style='left: #{x}px; top: #{y}px;' />").
        data("blockId", block_id).
        draggable(containment: "parent").css(position: "absolute")
      $(e.target).append(block)

  $("div.block").draggable(containment: "parent").css(position: "absolute")

  $(document).on "dragstop", "div.block", (e) ->
    block = $(e.target)
    blockId = block.data("blockId")
    x = parseInt(block.css("left"))
    y = parseInt(block.css("top"))
    $.ajax "/blocks/#{blockId}", type: "PUT", data: { block: { x: x, y: y } }
		
positionOfNewBlock = (e) ->
  canvas = $(e.target)
  x = e.pageX - canvas.position().left
  y = e.pageY - canvas.position().top
  [x, y]

5-7行目を次のように修正します:

      block = $("<div />").
        addClass("block").
        css(left: "#{x}px", top: "#{y}px").
        data("blockId", block_id).
        draggable(containment: "parent").
        css(position: "absolute")

$("div class='block' />") と書く代わりに $("<div />").addClass("block") と書いています。

また、$("div style='left: #{x}px; top: #{y}px;' />") と書く代わりに $("<div />").css(left: "#{x}px", top: "#{y}px") と書いています。

このようにclass属性やstyle属性は、文字列として埋め込まずに後からメソッドで設定することもできます。

正方形の代わりに画像を表示する

では、メールへの回答です。

本連載を今後も読み続ける方は、以下の修正を行う前に、現行のソースコードのバックアップを取ってください。次回以降は、この時点でのソースコードを起点にして話が進みます。

app/views/top/index.html.erb を修正。

<div id="canvas">
  <% @blocks.each do |block| %>
  <%= image_tag("rails.png",
      data: { :"block-id" => block.id },
      style: "left: #{block.x}px; top: #{block.y}px") %>
  <% end %>
</div>

3行目の content_tag(:div, nil, class: "block"image_tag("rails.png" に変更しています。

app/assets/stylesheets/top.css.scss を修正。

div#canvas {
  position: relative;
  width: 400px;
  height: 400px;
  background-color: silver;
  -moz-user-select: none;
  -khtml-user-select: none;
  -webkit-user-select: none;
  -o-user-select: none;  
  
  img {
    position: absolute;
    cursor: pointer;
  }
}

img要素にスタイルを指定しています。-moz-user-select などの指定は、キャンバスをダブルクリックしたときに画像が選択されないようにするためのものです。

app/assets/javascripts/top.js.coffee を修正。

$ ->
  $("div#canvas").dblclick (e) ->
    [x, y] = positionOfNewBlock(e)
    $.post '/blocks', block: { x: x, y: y }, (block_id) ->
      block = $("<img />").
        attr("src", "assets/rails.png").
        css(left: "#{x}px", top: "#{y}px").
        data("blockId", block_id).
        draggable(containment: "parent").
        css(position: "absolute")
      $(e.target).append(block)

  $("div#canvas img").draggable(containment: "parent").css(position: "absolute")

  $(document).on "dragstop", "div#canvas img", (e) ->
    block = $(e.target)
    blockId = block.data("blockId")
    x = parseInt(block.css("left"))
    y = parseInt(block.css("top"))
    $.ajax "/blocks/#{blockId}", type: "PUT", data: { block: { x: x, y: y } }
		
positionOfNewBlock = (e) ->
  canvas = $(e.target)
  x = e.pageX - canvas.position().left
  y = e.pageY - canvas.position().top
  [x, y]

変更箇所は4点です:

  • 5行目の $("<div />")$("<img />") に変更。
  • 6行目の addClass("block")attr("src", "assets/rails.png") に変更。
  • 13行目の $("div.block")$("div#canvas img") に変更。
  • 15行目の "div.block""div#canvas img" に変更。

img要素のsrc属性を指定するのにattrメソッドを使用している点に着目してください。jQueryはclass属性とstyle属性とdata-*を特別扱いしていて、それぞれにaddClasscssdataという専用メソッドを用意しています。それ以外の属性については、attrメソッドの第1引数に属性名を指定することで値を参照したり変更したりできます。

では、動作確認しましょう。ブラウザを再読込してから、ページ上の灰色の部分を何度かダブルクリックすると次のような表示になります。

coffee06-0

画像をドラッグして位置を変更できる点と、変更後にブラウザを再読込して画像の位置が変わらない点についても確認してください。