2016年3月1日火曜日

Using webfont.js from Dart (via package:js)

webfont.jsをDartから呼ぶ必要があって、今まではdart:jsを使っていたのだけどdart:jsが非推奨になってしまったので新しいpackage:jsへ書き換えることにした。ドキュメントがあまり整備されてなくて少し手間取ったので参考として残しておく。なお使用したバージョンは下記の通り。

  • Dart : 1.14.2
  • package:js : 0.6.0
  • webfont.js : 1.6.16

JavaScript版

var param = {
  google: {
    families: ['Droid Sans', 'Droid Serif']
  }
};
WebFont.load(param);

Dart(package:js)版

@JS("WebFont")
library web_font;

import "package:js/js.dart";

@JS()
external load(Config config);

@JS()
@anonymous
class GoogleGroup {
  external List<String> get families;
  external factory GoogleGroup({List<String> families});
}

@JS()
@anonymous
class Config {
  external GoogleGroup get google;
  external Function get active;
  external void set active(Function f);
  external factory Config({GoogleGroup google});
}

/// Initialize web font 
void init(List<String> fonts, {Function onActive: null}) {
  var gg = new GoogleGroup(families:fonts);
  var c = new Config(google: gg);
  c.active = allowInterop(onActive);
  load(c);
}

使い方はたとえばこんな感じ。

import 'package:web_font/web_font.dart' as WebFont;
...
var fontNames = ["Krona One", "Atomic Age"];
var f = () => Logger.root.info("Loaded font!");
WebFont.init(fontNames, onActive: f);
...

基本的にはJavaScript側へ渡すオブジェクトに対してクラス定義して修飾子つけてアノテーションつけて…という風に機械的にやってくことになる。自動的にラッパーを生成することもできるだろうし、実際Polymer.dartではPolymer.jsからcustom_element_apigenというツールを使ってAPIを自動的に生成している。

感想としては、こうして並べてみるとDartからJavaScript使うのエラい面倒だなという印象を受ける。自動的にAPIを生成してくれるものがあれば(SWIGみたいな?)それを使うのが楽かもしれない。

2016/3/13追記

Dart SDK 1.15リリースではさっきのコードで次のエラーが表示されるようになった。

Uncaught Unhandled exception:
Unhandled exception:
Unhandled exception:
Invalid argument (Expandos are not allowed on strings, numbers, booleans or null): null
#0      Expando._checkType (dart:core-patch/expando_patch.dart:134)
#1      Expando.[] (dart:core-patch/expando_patch.dart:14)
#2      allowInterop (dart:js:1532)
#3      init (package:xclamm/web_font/web_font.dart:29:14)
...

これを直すにはコードを下記のようにする必要がある

...
/// Initialize web font 
void init(List<String> fonts, {Function onActive: null}) {
  var gg = new GoogleGroup(families:fonts);
  var c = new Config(google: gg);
  if (onActive != null) {
    c.active = allowInterop(onActive);
  }
  load(c);
}

0 件のコメント:

コメントを投稿