Skip to content

Every little fish swimming around is a visitor on this page right now, and one of them is you ( ´ ▽ ` )ノ If they get in the way of reading, feel free to turn them off.

Pinball util ​

Make the web into a pinball machine, let's play pinball together ( ´ ▽ ` )ノ°

Tech Keywords ​

NameDescription
Matter-js2D physics engine
Physics SimulationSimulates real-world physics such as gravity, collisions, and velocity
Vector MathMath operations for direction, acceleration, velocity, and more
JS AnimationJavaScript-driven animation for more complex, precise control; popular libraries include GSAP and anime.js
Vue DirectiveCustom Vue directives that encapsulate DOM manipulation logic and repeated behaviors

Usage Examples ​

Basic Usage ​

Using the v-pinball-mech directive can turn the DOM into various mechanisms

When the left and right mouse buttons are pressed at the same time, the launcher can be opened. For touch devices, press and hold for a long time to open the launcher.

When the launcher appears, the ball will be stationary. Drag to adjust the direction and force, and release to launch the ball.


The pinball machine ( Pinball ) was invented by Raymond Moloney in 1934.Pinball machines typically feature various mechanical devices, such as springs 、 flippers , etc. These devices affect the ball's trajectory and provide additional challenge and funPinball was once considered gambling and was banned in the US for 40 years . It was finally legalized after Roger Sharpeproved in court that pinball is a game of skill, not chance .3D Pinball: Space Cadet was based on the prototype from Maxis 's publication Full Tilt! PinballLater, it was ported by Microsoft and included from Windows 95 through Windows XP in multiple versions of Windows.Due to issues like compatibility 、 maintenance costs , it was no longer included after Windows Vista .
View the example source code
vue
<template>
  <div class="relative w-full flex flex-nowrap">
    <div
      v-pinball-mech
      class="wall absolute bottom-0 left-0 hidden h-4 w-full rounded-full md:block"
    />

    <div
      v-pinball-mech
      class="wall hidden w-4 rounded-full md:block"
    />

    <div class="text w-full flex flex-col justify-center gap-10 md:p-10">
      <span>
        {{ t('history.p1.start') }}
        <b v-pinball-mech="bumperOptions">{{ t('history.p1.pinball_en') }}</b>
        {{ t('history.p1.middle') }}
        <ins v-pinball-mech="'spinner'">{{ t('history.p1.inventor') }}</ins>
        {{ t('history.p1.end') }}
      </span>

      <span>
        {{ t('history.p2.start') }}
        <b v-pinball-mech="bumperOptions">{{ t('history.p2.spring') }}</b>
        、
        <ins v-pinball-mech="'spinner'">{{ t('history.p2.flipper') }}</ins>
        {{ t('history.p2.middle') }}
        <ins v-pinball-mech="'spinner'">{{ t('history.p2.trajectory') }}</ins>
        {{ t('history.p2.connector') }}
        <b v-pinball-mech="bumperOptions">{{ t('history.p2.challenge') }}</b>
      </span>

      <span>
        {{ t('history.p3.start') }}
        <ins v-pinball-mech="'spinner'">{{ t('history.p3.duration') }}</ins>
        {{ t('history.p3.middle') }}
        <b v-pinball-mech="bumperOptions">{{ t('history.p3.hero') }}</b>
        <a
          href="https://en.wikipedia.org/wiki/Roger_Sharpe_(pinball)"
          target="_blank"
          rel="noopener"
        >
          {{ t('history.p3.link_text') }}
        </a>
        {{ t('history.p3.connector') }}
        <ins v-pinball-mech="'spinner'">
          {{ t('history.p3.skill') }}
        </ins>
        {{ t('history.p3.end') }}
      </span>

      <span>
        <ins v-pinball-mech="'spinner'">{{ t('history.p4.game_title') }}</ins>
        {{ t('history.p4.middle') }}
        <b v-pinball-mech="bumperOptions">{{ t('history.p4.company') }}</b>
        {{ t('history.p4.connector') }}
        <ins v-pinball-mech="'spinner'">{{ t('history.p4.original_title') }}</ins>
      </span>

      <span>
        {{ t('history.p5.start') }}
        <b v-pinball-mech="bumperOptions">{{ t('history.p5.microsoft') }}</b>
        {{ t('history.p5.middle_1') }}
        <ins v-pinball-mech="'spinner'">Windows 95</ins>
        {{ t('history.p5.middle_2') }}
        <ins v-pinball-mech="'spinner'">Windows XP</ins>
        {{ t('history.p5.end') }}
      </span>

      <span>
        {{ t('history.p6.start') }}
        <b v-pinball-mech="bumperOptions">{{ t('history.p6.compatibility') }}</b>
        、
        <b v-pinball-mech="bumperOptions">{{ t('history.p6.maintenance') }}</b>
        {{ t('history.p6.middle') }}
        <ins v-pinball-mech="'spinner'">Windows Vista</ins>
        {{ t('history.p6.end') }}
      </span>
    </div>

    <div
      v-pinball-mech
      class="wall hidden w-4 rounded-full md:block"
    />

    <div class="fixed left-0 z-[9999] w-screen flex translate-y-1/2 justify-around gap-6 opacity-60 -bottom-1">
      <div
        v-for="i in 35"
        :key="i"
        v-pinball-mech="bumperOptions"
        class="aspect-square w-6 shrink-0 rounded-full bg-orange-300"
      />
    </div>

    <util-pinball />
  </div>
</template>

<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import UtilPinball from '../util-pinball.vue'
import { vPinballMech } from '../v-pinball-mech'

const { t } = useI18n()

const bumperOptions = {
  type: 'bumper',
  radius: 999,
} as const
</script>

<style lang="sass" scoped>
.text
  b
    background: light-dark(#ffe685, #615630)
    padding: 0 0.2em
    white-space: nowrap
  ins
    white-space: nowrap

.wall
  background: light-dark(#F0F0F0, #3f3f46)
</style>

Principle ​

Use Matter.js for 2D physical simulation, and use Vue Directive to convert DOM elements into physical objects.

Source Code ​

API ​

Props ​

interface Props {
  /** 重力加速度
   *
   * x, y 為加速度的方向,scale 為加速度的大小
   */
  gravity?: Matter.Gravity;
  ballRadius?: number;
}

v0.90.7