LoginSignup
24
23

More than 5 years have passed since last update.

SVGでMaterialDesign風のボタンをつくってみた

Posted at

ワンパクでは派手な技術よりも役に立つビジネスに貢献できることが求められているがゆえに技術者とかデザインをやればやるほどなんだか無難で地味なものばかりやる方向に行ってしまいがちでこれではいかんという思いからイケてる技術をつかってイケてるUIをつくろうという思想でSVGやWebGLでUIを作っていくマイブームが到来しました。

MaterialDesign風のボタンのDEMO

SVGでボタンをつくってみて

コード解説

var unit = 4; // とりあえず4dpの単位をつくる
var width = 38;
var height = 12;
var radius = 32;

// SVGを突っ込む要素のIDを渡す
// だいたいチェーンメソッド
var draw = SVG('button').size(unit * width, unit * height + unit);

// ピンクの下地
var base = draw.rect(unit * width, unit * height);
base.radius(unit, unit).fill('#f06');

// 演出用のレイヤーたちをクリップするためのグループ
var group = draw.group();

// クリップマスク用のシェイプ
var clipRect = draw.rect(unit * width, unit * height);
clipRect.radius(unit, unit);

// ホバーしたとき用の白めのシェイプ
var focusCircle = group.ellipse(unit * radius, unit * radius);
focusCircle.center(unit * width * 0.5, unit * height * 0.5);
focusCircle.fill('#fff').opacity(0);
focusCircle.filter(function(add) {
  add.gaussianBlur(1); // ガウスエフェクト
});

// プレスしたとき用の黒めのシェイプ
var pressedCircle = group.ellipse(unit * radius, unit * radius);
pressedCircle.center(unit * width * 0.5, unit * height * 0.5);
pressedCircle.fill('#000').opacity(0);
pressedCircle.filter(function(add) {
  add.gaussianBlur(1); // ガウスエフェクト
});

// ボタンのラベル
var labelText = group.text('Button');
labelText.font({
  family : 'Helvetica',
  size   : unit * 4,
  anchor : 'middle'
});
labelText.x(unit * width * 0.5);
labelText.dy(unit * 2);
labelText.fill('#fff');

// クリップ作成
var clip = draw.clip().add(clipRect);
group.clipWith(clip);

// ドロップシャドウ
base.filter(function(add) {
  var blur = add.offset(0, 2).in(add.sourceAlpha).gaussianBlur(1);
  // ドロップシャドウを半透明にする処理
  blur = blur.colorMatrix('matrix', [
    0, 0, 0, 0, 0,
    0, 0, 0, 0, 0,
    0, 0, 0, 0, 0,
    0, 0, 0, 0.5, 0
  ]);
  add.blend(add.source, blur);
});

function onFocus() {
  var loopFlag = false;
  function loop() {
    var r = loopFlag ? radius : radius - 2;
    this.animate(1600, '>').size(unit * r, unit * r).after(loop);
    loopFlag = !loopFlag;
  }

  focusCircle.stop().animate(450, '>', 250).opacity(0.5).after(loop);
  pressedCircle.stop().animate(200).size(unit * radius, unit * radius).opacity(0);
}
function onBlur() {
  focusCircle.stop().animate(800).opacity(0).size(unit * radius, unit * radius);
}
function onPress(event) {
  var innerRadius = radius - 12;
  var outerRadius = radius + 4;
  pressedCircle.center(event.layerX, event.layerY).opacity(0.2).size(unit * innerRadius, unit * innerRadius)
    .animate(600, '>').size(unit * outerRadius, unit * outerRadius).opacity(0);

  focusCircle.stop().animate(200).opacity(0).size(unit * radius, unit * radius);
}

// イベント設定
group.click(onPress);
group.touchstart(onFocus);
group.touchend(onBlur);
group.mouseover(onFocus);
group.mouseout(onBlur);

技術的なポイント

基本

var draw = SVG('target');

SVG要素を生成する。

要素作成

var rect = draw.rect(30, 30);

SVGの子要素として四角をつくる。

グルーピング

var group = draw.group();
var rect = group.rect(30, 30);

group要素の子要素に四角をつくる。

クリッピングマスク

var base  = draw.rect(100, 40);
var rect = draw.rect(); //クリップするシェイプ
var clip = draw.clip().add(rect); // クリップを作成してシェイプを追加

base.clipWith(clip); // クリップの適応

四角とかだけじゃなくて、groupにも適応できる。

アニメーション

rect.animate().size(100, 100);

アニメするためのプロキシオブジェクトをつくってそれにプロパティ指定するとうごくぽい。

rect.animate(5000, '>', 1000).size(100, 100);

引数に duration, easing, delayを設定できる。

フィルター

rect.filter(function(add) { // 要素に適応されるフィルタオブジェクト
  add.gaussianBlur(1); // ガウスエフェクト
});

プラグインを入れると .filter オブジェクトが追加されます。

イベント処理

click, mouseXXX, touchXXXあたりはとりあえず使えました。

rect.click(function(event) {
  // do something.
});

現場からは以上です。
ヒマがあったらもうすこしかきます。

24
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
23