Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Auto Save

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                #canvas.canvas
small.small ※タッチデバイスはiframeの関係なのかCodePenでは認識しません
              
            
!

CSS

              
                .canvas
  width 500px
  height 300px
  background #cf2620
              
            
!

JS

              
                const { fromEvent, of } = rxjs
const { throttleTime, map, delay } = rxjs.operators

interface HanabiFrame {
  /** 全面のリングの中心を広げる */
  expandedRing: Konva.Tween

  /** 赤円を広げる */
  expandedRedCircle: Konva.Tween

  /** レイヤーを広げる */
  scaleUpLayer: Konva.Tween
}

// グループサイズ
const size = 40

// 一番下のスポーク部分
const spoke = new Konva.Path({
  x: 0,
  y: 0,
  width: 80,
  height: 80,
  data:
    'm33.9644661 42.5h-33.9644661v-5h33.9644661l-24.0165043-24.0165043 3.5355339-3.5355339 24.0165043 24.0165043v-33.9644661h5v33.9644661l24.0165043-24.0165043 3.5355339 3.5355339-24.0165043 24.0165043h33.9644661v5h-33.9644661l24.0165043 24.0165043-3.5355339 3.5355339-24.0165043-24.0165043v33.9644661h-5v-33.9644661l-24.0165043 24.0165043-3.5355339-3.5355339z',
  fill: '#fff'
})

// 星の上に置く赤円
const circle = new Konva.Circle({
  x: size,
  y: size,
  radius: size,
  scale: {
    x: 0.3,
    y: 0.3
  },
  fill: '#cf2620'
})

// 一番上の円
const ring = new Konva.Ring({
  x: size,
  y: size,
  innerRadius: 0,
  outerRadius: size,
  fill: '#fff'
})

class Hanabi {
  private readonly _item: Konva.Group

  private readonly _spoke: Konva.Path
  private readonly _circle: Konva.Circle
  private readonly _ring: Konva.Ring

  public constructor(x: number, y: number) {
    this._item = new Konva.Group({
      x,
      y,
      offset: {
        x: size,
        y: size
      },
      scale: {
        x: 0.1,
        y: 0.1
      }
    })

    // 花火データ生成
    this._spoke = spoke.clone()
    this._item.add(this._spoke)

    this._circle = circle.clone()
    this._item.add(this._circle)

    this._ring = ring.clone()
    this._item.add(this._ring)
  }

  /** 花火データ */
  public item(): Konva.Group {
    return this._item
  }

  /** 花火アニメーション */
  public animation(cb?: () => void): void {
    of(this._tween(cb))
      .pipe(
        map(
          (list: HanabiFrame): HanabiFrame => {
            list.scaleUpLayer.play()
            return list
          }
        ),
        map(
          (list: HanabiFrame): HanabiFrame => {
            list.expandedRing.play()
            return list
          }
        ),
        delay(50),
        map(
          (list: HanabiFrame): HanabiFrame => {
            list.expandedRedCircle.play()
            return list
          }
        )
      )
      .subscribe((): void=>{
      this._event.once('finish', (): void => {
        // if (cb) cb()
      })
    })
  }

  /**
   * アニメーション定義
   * @private
   */
  private _tween(cb?: () => void): HanabiFrame {
    const expandedRing = new Konva.Tween({
      node: this._ring,
      innerRadius: size,
      duration: 0.1,
      easing: Konva.Easings.StrongEaseIn,
      onFinish: (): void => {
        expandedRing.destroy()
      }
    })

    const expandedRedCircle = new Konva.Tween({
      node: this._circle,
      scaleX: 1.1,
      scaleY: 1.1,
      duration: 0.5,
      easing: Konva.Easings.EaseOut,
      onFinish: (): void => {
        expandedRedCircle.destroy()
      }
    })

    const scaleUpLayer = new Konva.Tween({
      node: this._item,
      scaleX: 2.5,
      scaleY: 2.5,
      duration: 1.5,
      easing: Konva.Easings.EaseOut,
      onFinish: (): void => {
        scaleUpLayer.destroy()
        if (cb) cb()
      }
    })

    return {
      expandedRing,
      expandedRedCircle,
      scaleUpLayer
    }
  }
}



// Run
const stage = new Konva.Stage({
  container: 'canvas',
  width: 500,
  height: 300
})

// 花火レイヤー
const hanabiLayer = new Konva.Layer({x: 0, y: 0})
stage.add(hanabiLayer)

const clickHere = new Konva.Layer({x: 0 ,y: 0});
clickHere.add(new Konva.Text({
  text: '好きなところをクリック',
  width: 500,
  fontSize: 14,
  align: 'center',
  fill: '#fff',
  x: 0,
  y: 300 / 2,
  offset: {
    x: 0,
    y: 7
  }
}))

stage.add(clickHere)
stage.draw()

// イベント
fromEvent(stage, 'mouseup touchend pointerup')
  .pipe(throttleTime(80))
  .subscribe((): void => {
    const { x, y } = stage.getPointerPosition()

    const hanabi = new Hanabi(x, y)
    const item = hanabi.item()

    hanabiLayer.add(item)
    hanabiLayer.draw()


  hanabi.animation((): void => {
    // CodePenだと遅延させないとエラーを起こす
    setTimeout((): void =>{
      item.destory()
    }, 100)
  })
})
              
            
!
999px

Console