Haxe/JSの練習のためにスライドショーっぽいものを作る


slideshow

作ったスライドショーっぽいもの

JavaScriptを書き出すための言語はいくつかありますが、CoffeeScriptはちょっと見た程度。TypeScriptは導入してみようと思ったもののちょうど大きな変更があったようで、もうちょっと待ったほうが良さそうだったのと、外部ファイルの参照がやや面倒そうだったので後回し。JSXはPhotoshop等のJavaScript拡張と名前が被っていて混同するのであんまり。AS3はそこそこ使えるというのもあったので、Haxeならわりと楽に使えるかなということでHaxeを覚えてみることにしました。

(使用したのはHaxe3.0、OSはWindows7です。)

導入

インストールはインストーラでさくっと一発でした。

Download – Haxe

HelloWorld

HelloWorldまでの方法はこちらに書かれているので真似して書けばOK。コンパイルがファイルダブルクリックでできるのが新鮮な感じ。

Getting started with Haxe/JS – Haxe

おもむろにスライドショーを作る

以下の様なディレクトリ構成で作ることにしました。libsとsrcでわざとファイルを分けて、使いまわすものはlibsに、その場で使うだけのものはsrcに、とAS3での開発の時はしていたのでそれにならってHaxeでもそうしてみてます。

ディレクトリ構成

│  index.html
│  main.js
│  style.css
│  
├─haxe
│  │  compile.hxml
│  │  
│  ├─libs
│  │  └─net
│  │      └─spiffield
│  │          └─haxe
│  │              └─display
│  │                 └─slideshow
│  │                         SlideShow.hx
│  │                      
│  └─src
│          Main.hx
│          
└─img
        image1.png
        image2.png
        image3.png

コンパイル用のhxmlは以下のようにしています。

-cp src
-cp libs
-js ../main.js
-main Main

あんまり説明を見かけませんでしたが、「-cp <path>」というのは複数書けば複数認識するようでした。

書いたHaxe

src/Main.hx

import net.spiffield.haxe.display.slideshow.SlideShow;
import js.JQuery;
import js.html.Document;
class Main{
	static function main(){
		new JQuery(js.Browser.window).ready(init);
	}
	static function init(e:JqEvent):Void{
		var slideshow:SlideShow = new SlideShow(640, 360, ["./img/image1.png", "./img/image2.png", "./img/image3.png"]);
	}
}

libs/net/spiffield/haxe/display/slideshow/SlideShow.hx

package net.spiffield.haxe.display.slideshow;
import js.JQuery;
import js.html.Image;
import js.html.Element;
import haxe.Timer;

class SlideShow{

	static private inline var TARGET_ID:String = "slideshow";
	static private inline var IMG_LIST_ID:String = "slideshowImages";
	static private inline var ANCHOR_LIST_ID:String = "slideshowAnchors";
	static private inline var STEP_TIME:Int = 3000;
	static private inline var STEP_TIME_USER_ACTION:Int = 5000;

	private var _w:Int;
	private var _h:Int;
	private var _timer:Timer;
	private var _currentIndex:Int = 1;
	private var _imgNum:Int = 0;

	/**
	 * Constructor
	 */
	public function new(width:Int, height:Int, pathList:Array<String>){
		_w = width;
		_h = height;

		createHTML(pathList);
		setTimer();
	}

	/**
	 * HTML生成
	 */
	private function createHTML(pathList:Array<String>):Void{
		var container:JQuery = _jq("#"+TARGET_ID);

		var imgList:Element = _newElem("ul");
		var anchorList:Element = _newElem("ul");
		imgList.id = IMG_LIST_ID;
		anchorList.id = ANCHOR_LIST_ID;

		_imgNum = pathList.length;
		for(i in 0..._imgNum){
			var path:String = pathList[i];

			var imgTagStr:String = '<li id="img${(i+1)}" style="left:${i*_w}px"><img src="$path" width="$_w" height="$_h" /></li>';
			var anchorTagStr:String = '<li id="anchor${i+1}"><a href="#slideshow_${(i+1)}">${(i+1)}</a></li>';

			imgList.innerHTML += imgTagStr;
			anchorList.innerHTML += anchorTagStr;

		}

		container.append(imgList);
		container.append(anchorList);

		_jq("#" + anchorList.id + " a").on("click", changeImg);
		_jq("#anchor1").addClass("current");
	}

	/**
	 * 画像変更
	 */
	private function changeImg(e:JqEvent):Void{
		var nextTime:Int = STEP_TIME;
		_timer.stop();
		var nextIndex:Int = 0;
		if(e != null){
			nextTime = STEP_TIME_USER_ACTION;
			nextIndex = Std.parseInt(e.currentTarget.getAttribute("href").substr(-1));
		}
		else{
			nextIndex = _currentIndex + 1;
			if(nextIndex > _imgNum){
				nextIndex = 1;
			}
		}
		_jq("#img" + nextIndex).css({left:_w}).animate({left:0}, 670);
		_jq("#img" + _currentIndex).css({left:0}).animate({left:-_w}, 670);
		_jq("#"+ANCHOR_LIST_ID+" li").removeClass("current");
		
		_currentIndex = nextIndex;

		_jq("#anchor" + _currentIndex).addClass("current");

		_timer = Timer.delay(function():Void{changeImg(null);}, nextTime);
	}

	/**
	 * タイマー設定
	 */
	private function setTimer():Void{
		_timer = Timer.delay(function():Void{changeImg(null);}, STEP_TIME);
	}

	/**
	 * jQuery呼び出し
	 */
	private function _jq(str:String):JQuery{
		return new JQuery(str);
	}

	/**
	 * createElementショートカット
	 */
	private function _newElem(name:String):Element{
		return js.Browser.document.createElement(name);
	}
}

書けてしまえば、ここに貼りつけたもの以上でも以下でもないのですが、HaxeからjQuery使う方法とかいろいろ悩んだのであとで思い出せるようにメモ。

  • document.readyがうまくいかない?
  • constという概念がない
  • forの書き方が違う
  • シングルクォーテーションで変数展開できる
  • haxeのTimerは使いにくい?

document.readyがうまくいかない?

Main.hxに

new JQuery(js.Browser.window).ready(init);

と書いてdocument.readyの代わりにしているんですが、js.Browser.windowではなくjs.Browser.documentと書くと、コンパイルで弾かれました。new JQueryの引数はjs.html.Elementだそうですが、js.Brwoser.documentはElementは継承していないのでしょうか。

constという概念がない

知っていればどうということもないのですが、Haxe自体にconstがありません。static private inline varなどとすれば定数みたいな扱いになるようです。

forの書き方が違う

慣れればまあ…みたいなところではあるんですが、他のプログラミング言語でよくある

for(var i:int = 0; i &lt; num; i++){hoge(i);}//AS3風

のような書き方ができません。

for(i in 0...num){hoge(i);}

と書く必要があります。減らしていくこともできないみたいなので微妙ではないかと思うんですが、他と一緒のほうが使いやすいのでは、とちょっと思います。

シングルクォーテーションで変数展開できる

‘”no”${i+1}’みたいなので計算結果を代入してくれます。便利ではあるんですがPHP使ってるとなんか逆のような印象を受けます。

haxeのTimerは使いにくい?

newしてrunして実行するだけなら別にいいんですが、後で実行タイミングを変える方法がわからなかったので結局生JSのsetTimeoutを使いました。delayも指定秒後に実行するだけでsetTimeoutと変わらないような…?

まとめ

HaxeでJavaScriptを書き出すためには、JavaScriptをそこそこ書けないと厳しいと思います。いきなりJavaScriptを知らずに突っ込むと魔界に連れ込まれそうな印象です。やはり使い始めの言語だけあって、問題がどこにあるのか判断しにくい。Haxeの問題かと思えばJSの問題だったり。その逆だったり。(document.readyの後にHTMLを操作しないとDOM要素取得できないのは当たり前なんですが、Haxeになった途端にそこを忘れてたりしました。)

そうはいいながら、AS3をわりと仕事でメインでしばらく使っていたのもあって、Haxeの文法は(一部を除いて)とっつきやすいものでした。今回のような小規模なものであれば別にJSで作っても大差無さそうですが、規模が大きくなるとやっぱり生のJSだと管理しにくい部分もある気はしています。案外簡単に使えたので、もうしばらく使って見ることにします。Haxe。

ファイル一式

「Haxe/JSの練習のためにスライドショーっぽいものを作る」への1件のフィードバック

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です