2024年12月19日木曜日

Mo.jsのアニメーションをOracle APEXのアプリケーションに表示する

Mo.jsのアニメーションをOracle APEXのアプリケーションに表示してみます。Mo.jsのTutorialsのGetting startedにあるbouncyCircleとspinnerの表示に取り組みます。

Mo.jsのライブラリはESモジュールとして読み込みます。ライブラリの読み込みからアニメーションの描画を行うコードは別のファイルに記述し、Oracle APEXのページにはコードを一切埋め込まないようにします。

作成したAPEXのアプリケーションは以下のように動作します。Mo.jsのプレイヤーなしのページとプレイヤーありのページを作成します。


以下より簡単に実装について紹介します。

空のAPEXアプリケーションを作成します。名前Play Mo.jsとしました。

ホーム・ページに、プレイヤーなしのbouncyCirclespinnerのアニメーションを表示します。

静的コンテンツのリージョンを作成し、ソースHTMLコードとして以下を記述します。bouncyCircleのアニメーションを描画するDIV要素になります。

<div id="bouncyCircle" class="w95p h300"></div>


同様に静的コンテンツのリージョンを作成します。ソースHTMLコードとして以下を記述します。spinnerのアニメーションを描画するDIV要素になります。

<div id="spinner" class="w95p h300"></div>


ページ・プロパティJavaScriptファイルURLに以下を記述します。

[module]#APP_FILES#app-mojs#MIN#.js

HTMLヘッダーimportmapを記述します。ES6モジュールを対象としたCDNサービスのesm.shを指しています。
<script type="importmap">
    {
        "imports": {
            "@mojs/core": "https://esm.sh/@mojs/core"
        }
    }
</script>

静的アプリケーション・ファイルとしてapp-mojs.js(およびミニファイしたapp-mojs.min.js)を作成します。内容として以下を記述します。ほとんどMo.jsのGetting startedそのままです。

import mojs from "@mojs/core";
const bouncyCircle = new mojs.Shape({
parent: '#bouncyCircle',
shape: 'circle',
fill: { '#F64040': '#FC46AD' },
radius: { 20: 80 },
duration: 2000,
width: 200,
height: 200,
isYoyo: true,
isShowStart: true,
easing: 'elastic.inout',
repeat: 3,
});
bouncyCircle.play();
const spinner = new mojs.Shape({
parent: '#spinner',
shape: 'circle',
stroke: '#FC46AD',
strokeDasharray: '125, 125',
strokeDashoffset: { '0': '-125' },
strokeWidth: 4,
fill: 'none',
left: '50%',
top: '50%',
rotate: { '-90': '270' },
radius: 20,
isShowStart: true,
duration: 2000,
easing: 'back.in',
})
.then({
rotate: { '-90': '270' },
strokeDashoffset: { '-125': '-250' },
duration: 3000,
easing: 'cubic.out',
});
spinner.play();
view raw app-mojs.js hosted with ❤ by GitHub


プレイヤーなしのバージョンは以上で完成です。

空白ページをページ番号として作成し、ホーム・ページと同じく静的コンテンツのリージョンbouncyCirclespinnerを作成します。

ページ・プロパティJavaScriptファイルURLに以下を記述します。

[module]#APP_FILES#app-mojs-player#MIN#.js

CSSファイルURLに以下を記述します。

#APP_FILES#app-mojs-player#MIN#.css

HTMLヘッダーに以下を記述します。
<script type="importmap">
    {
        "imports": {
            "@mojs/core": "https://esm.sh/@mojs/core",
            "@mojs/player": "https://esm.sh/@mojs/player"
        }
    }
</script>

静的アプリケーション・ファイルとしてapp-mojs-player.js(およびミニファイしたapp-mojs-player.min.js)を作成します。内容として以下を記述します。

import mojs from "@mojs/core";
import MojsPlayer from "@mojs/player";
/*
* Player付きbouncyCircle
*/
const bouncyCircleEl = document.getElementById("bouncyCircle");
const bouncyCircle = new mojs.Shape({
parent: bouncyCircleEl,
shape: 'circle',
fill: {'#F64040': '#FC46AD'},
radius: {20: 80},
duration: 2000,
width: 200,
height: 200,
isYoyo: true,
isShowStart: true,
easing: 'elastic.inout',
repeat: 3
});
const mojsPlayerBC = new MojsPlayer({
// required
add: bouncyCircle,
parent: bouncyCircleEl,
// optionally
className: 'my-mojs-player', // class name to add to main HTMLElement
isSaveState: true, // determines if should preserve state on page reload
isPlaying: false, // playback state
progress: 0, // initial progress
isRepeat: false, // determines if it should repeat after completion
isBounds: false, // determines if it should have bounds
leftBound: 0, // left bound position [0...1]
rightBound: 1, // right bound position [0...1]
isSpeed: false, // determines if speed control should be open
speed: 1, // `speed` value
isHidden: false, // determines if the player should be hidden
precision: 0.1, // step size for player handle - for instance, after page reload - player should restore timeline progress - the whole timeline will be updated incrementally with the `precision` step size until the progress will be met.
name: 'mojs-player-bc', // name for the player - mainly used for localstorage identifier, use to distinguish between multiple local players
onToggleHide(isHidden) { // should be called after user taps on the hide-button (isHidden is a boolean, indicating the visibility state of the player)
if (isHidden) {
// do something when player is invisible
} else {
// do something when player is visible
}
}
});
// bouncyCircle.play();
/*
* Player付きspinner
*/
const spinnerEl = document.getElementById("spinner");
const spinner = new mojs.Shape({
parent: spinnerEl,
shape: 'circle',
stroke: '#FC46AD',
strokeDasharray: '125, 125',
strokeDashoffset: { '0': '-125' },
strokeWidth: 4,
fill: 'none',
left: '50%',
top: '50%',
rotate: { '-90': '270' },
radius: 20,
isShowStart: true,
duration: 2000,
easing: 'back.in',
})
.then({
rotate: { '-90': '270' },
strokeDashoffset: { '-125': '-250' },
duration: 3000,
easing: 'cubic.out',
});
const mojsPlayerSP = new MojsPlayer({
// required
add: spinner,
parent: spinnerEl,
// optionally
className: 'my-mojs-player', // class name to add to main HTMLElement
isSaveState: true, // determines if should preserve state on page reload
isPlaying: false, // playback state
progress: 0, // initial progress
isRepeat: false, // determines if it should repeat after completion
isBounds: false, // determines if it should have bounds
leftBound: 0, // left bound position [0...1]
rightBound: 1, // right bound position [0...1]
isSpeed: false, // determines if speed control should be open
speed: 1, // `speed` value
isHidden: false, // determines if the player should be hidden
precision: 0.1, // step size for player handle - for instance, after page reload - player should restore timeline progress - the whole timeline will be updated incrementally with the `precision` step size until the progress will be met.
name: 'mojs-player-sp', // name for the player - mainly used for localstorage identifier, use to distinguish between multiple local players
onToggleHide(isHidden) { // should be called after user taps on the hide-button (isHidden is a boolean, indicating the visibility state of the player)
if (isHidden) {
// do something when player is invisible
} else {
// do something when player is visible
}
}
});
// spinner.play();


Mo.jsのドキュメントからはプレイヤーの位置を指定する方法を見つけられず、コードから見つけています。MojsPlayerのインスタンスを作成する際に、parentとしてアニメーションを描画するDIV要素を指定し、classNameに指定したCSSクラス(上記のコードではmy-mojs-player)で表示位置を決めます。デフォルトでは、ウィンドウの最下部にプレイヤーが表示されます(parentの指定がdocument.bodyになる)。

プレイヤーの表示位置を定義するCSSクラスmy-mojs-playerは、app-mojs-player.cssで定義します。静的アプリケーション・ファイルとして作成します。

.my-mojs-player {
position: absolute !important;
bottom: 0 !important;
}


以上でアプリケーションは完成です。

今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/play-mojs.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。