見出し画像

自作プラグイン解説:スライダーUIプラグイン【ティラノスクリプト】

自作プラグイン解説第三弾です。
今回はこちらのプラグイン

わりと最初の頃につくったプラグインなので粗もあるんですが、とりあえず解説していきます。

あと、今回はこれまでと違って「タグを作成する」という手法を取っているので、独自タグを作ってみたい方は参考になるかなと思います。

プラグイン概要

ティラノスクリプトv5向けのスライダー・スイッチUIプラグインです。
コンフィグ画面での使用を想定しています。

【できること】
・スライダーを表示するタグ「slider」を使用できるように
・スイッチを表示するタグ「switch」を使用できるように

スライダーはこういうの↓

画像1

スイッチはこういうの↓

画像2

プラグイン構成

プラグインフォルダ内のファイル構成はこんな感じです。

slider_uiinit.ksmain.jsslider.cssreadme.txt

init.ks

それではまずは「init.ks」の中を見ていきます。

[loadjs storage="plugin/slider_ui/main.js"]
[loadcss file="./data/others/plugin/slider_ui/slider.css"]

[return]

めっちゃ単純ですね。
main.js」と「slider.css」を読み込んでいるだけです。

ティラノスクリプトのタグ構成

ティラノスクリプトのタグは、規定のプロパティおよびメソッドを持ったオブジェクトとして構成されています。

規定のプロパティは以下の通り

・vital:必須パラメータ名
・pm:使用可能なパラメータ及びその初期値
・start:タグを記述したときに実行する処理
・setEvent:ボタンなどをクリックしたときに実行する処理

他、タグによって追加のメソッドがあったりしますが、今回使用しているのは上記の4つです。

startとsetEventの違いですが、例えば[button]タグを想像してください。

[button]タグを記述したときに実行されるのはstart関数です。ここでは、ボタンを配置する処理を行います。
そして配置されたボタンをクリックしたときに実行される処理はsetEvent関数で定義します。

これを分けているのは、主にロード時の処理のためです。

ティラノではセーブデータ内に、セーブ時点でのDOM構成も保存しています。これにより、表示しているボタンなどもロード時にそのまま展開されます。

しかし、ボタンに定義されていたクリックイベントなどはセーブデータに保存されません。
なので、ロード時に改めてボタンにイベントを定義していきます。

start関数とsetEvent関数を分けておくことで、ロード時には各ボタンのsetEvent関数だけを実行すればボタンが動くようになるというわけです。

「slider」タグを追加

スライダーを表示するタグを追加します。

//スライダー
tyrano.plugin.kag.tag.slider = {
   vital: [
       "x", "y", "name",
   ],
   pm: {
       name: "",
       x: "",
       y: "",
       width: "400",
       height: "4",
       min: "0",
       max: "100",
       step: "1",
       border: "0",
       border_color: "white",
       border_radius: "30",
       thumb_width: "30",
       thumb_height: "30",
       thumb_radius: "30",
       thumb_color: "white",
       thumb_border: "0",
       thumb_border_color: "white",
       thumb_img: "",
       base_color: "#999",
       active_color: "white",
       storage: "",
       target: "",
       exp: "",
       preexp: "",
       var: "",
       tip_width: "0",
       tip_height: "0",
       tip_color: "white",
       tip_radius: "0",
       tip_text_color: "black",
       tip_text_size: "24",
       tip_tail: "true",
       tip_pos: "top",
       tip_margin: "0",
   },
   start: function(pm){
       const that = TYRANO
       const _pm = pm
       const name = _pm.name
       const variable = that.kag.embScript(_pm.var)
       const layer = TYRANO.kag.layer.getLayer("fix")
       let wrap = $('<div class="range fixlayer"></div>')
       let base = $('<div class="range_base"></div>')
       let active = $('<div class="range_active"></div>')
       let input = $('<input type=range>')
       let tip = $('<div class="range_tip"></div>')
       wrap.addClass(name)
       tip.addClass(name)
       input.attr({
           name: name,
           min: _pm.min,
           max: _pm.max,
           step: _pm.step,
       })
       input.attr({
           id: "input_" + name,
           value: variable,
       })
       base.css({
           background: __slider_ui.convertColor(_pm.base_color),
           position: "absolute",
           top: _pm.y + "px",
           left: _pm.x + "px",
           width: _pm.width + "px",
           height: _pm.height + "px",
           "border": _pm.border + "px solid " +  __slider_ui.convertColor(_pm.border_color),
           "border-radius": _pm.border_radius + "px",
       })
       active.css({
           background: __slider_ui.convertColor(_pm.active_color),
           position: "absolute",
           top:  parseInt(_pm.border) + parseInt(_pm.y) + "px",
           left: parseInt(_pm.border) + parseInt(_pm.x) + "px",
           height: _pm.height + "px",
           width: _pm.width + "px",
           "border-radius": _pm.border_radius + "px",
       })
       if(_pm.thumb_img !== ""){
           input.css({
               top: _pm.y + "px",
               left: _pm.x + "px",
               width: _pm.width + "px",
               height: _pm.height + "px",
               "--thumb-width": _pm.thumb_width + "px",
               "--thumb-height": _pm.thumb_height + "px",
               "--thumb-img": `url(../../../image/${_pm.thumb_img})`,
           })    
       }else{
           input.css({
               top: _pm.y + "px",
               left: _pm.x + "px",
               width: _pm.width + "px",
               height: _pm.height + "px",
               "--thumb-width": _pm.thumb_width + "px",
               "--thumb-height": _pm.thumb_height + "px",
               "--thumb-radius": _pm.thumb_radius == "0" ? "none" : _pm.thumb_radius + "px",
               "--thumb-color": __slider_ui.convertColor(_pm.thumb_color),
               "--thumb-border": _pm.thumb_border + "px solid " + __slider_ui.convertColor(_pm.thumb_border_color),
           })    
       }
       let _top = 0
       let _tail_top = "none"
       let _tail_bottom = "none"
       let _tail_arrow = ""
       let _tail = "none"
       if(_pm.tip_tail == "true"){
           _tail = "block"
       }
       if(_pm.tip_pos == "top"){
           _top = parseInt(_pm.y) - parseInt(_pm.tip_height) - parseInt(_pm.tip_margin) + "px"
           _tail_top = parseInt(_pm.tip_width) + "px solid " + _pm.tip_color
           _tail_arrow = parseInt(_pm.tip_height) / 2 * 1
       }else{
           _top = parseInt(_pm.y) + parseInt(_pm.tip_margin) + "px"
           _tail_bottom = parseInt(_pm.tip_width) + "px solid " + _pm.tip_color
           _tail_arrow = parseInt(_pm.tip_height) / 2 * -1
       }
       tip.css({
           position: "absolute",
           top: _top,
           opacity: 0,
           "z-index": "999",
           width: _pm.tip_width + "px",
           height: _pm.tip_height + "px",
           background: __slider_ui.convertColor(_pm.tip_color),
           color: __slider_ui.convertColor(_pm.tip_text_color),
           "font-size": _pm.tip_text_size + "px",
           "text-align": "center",
           "line-height": _pm.tip_height + "px",
           "border-radius": _pm.tip_radius + "px",
           "--tip_width": _pm.tip_width + "px",
           "--tip_height": _pm.tip_height + "px",
           "--tip_color": __slider_ui.convertColor(_pm.tip_color),
           "--tip_tail": _tail,
           "--tip_tail_top": _tail_top,
           "--tip_tail_bottom": _tail_bottom,
           "--tip_tail_arrow": _tail_arrow + "px"
       })
       
       wrap.append(base).append(active).append(input)
       if(_pm.tip_width != "0"){
           wrap.append(tip)
       }
       layer.append(wrap)
       __slider_ui.updateRange(_pm.name, _pm.width)
       that.kag.event.addEventElement({
           tag: "slider",
           j_target: input,
           pm: pm,
       })
       this.setEvent(input, pm)
       that.kag.ftag.nextOrder()
   },
   setEvent: function(input, pm){
       const that = TYRANO
       const _pm = pm
       //ツマミを動かしているとき
       input.on("input", function(e){
           $("." + _pm.name).find(".range_tip").css({
               opacity: 1,
           })
           __slider_ui.updateRange(_pm.name, _pm.width)
           that.kag.embScript(_pm.var + " = " + this.value)
           if(_pm.exp != ""){
               that.kag.embScript(_pm.exp, _pm.preexp)
           }
       })
       //ツマミを動かし終わったとき
       input.on("change", function(){
           $("." + _pm.name).find(".range_tip").css({
               opacity: 0,
           })
           //fixレイヤの場合はcallでスタックが積まれる
           if (_pm.storage != "" || _pm.target != "") {
               //コールスタックが帰ってきてない場合は、実行しないようにする必要がある
               //fixの場合はコールスタックに残る。
               var stack_pm = that.kag.getStack("call"); //最新のコールスタックを取得
               if(stack_pm == null){
                   //strong_stopの場合は反応しない
                   var _auto_next = _pm.auto_next;
                   if(that.kag.stat.is_strong_stop == true){
                       _auto_next = "stop";
                   }
                   //call実行
                   $("input").prop("disabled", true)
                   that.kag.ftag.startTag("call", {
                       storage: _pm.storage,
                       target: _pm.target,
                       auto_next: _auto_next
                   });
               }else{
                   //スタックが残っている場合
                   that.kag.log("callスタックが残っている場合、fixボタンは反応しません");
                   that.kag.log(stack_pm);
                   return false;
               }
           }
       })
       input.on("mouseup", function(){
           $("." + _pm.name).find(".range_tip").css({
               opacity: 0,
           })
       })
   
   }
}
tyrano.plugin.kag.ftag.master_tag.slider = tyrano.plugin.kag.tag.slider
tyrano.plugin.kag.ftag.master_tag.slider.kag = tyrano.plugin.kag

const __slider_ui = {
   updateRange:  function(name, w){
       var rangeValue = $("#input_" + name).val();
       var active = w * ((rangeValue - $("#input_" + name).attr("min")) / ($("#input_" + name).attr("max") - $("#input_" + name).attr("min")));
       var param = {width: active + "px"}
       let left = $("#input_" + name).css("left").replace("px", "")
       $("." + name).find(".range_active").css(param);
       let tip = $("." + name).find(".range_tip")
       if(tip.length > 0){
           tip.css({
               left: parseInt(left) + parseInt(active) - (parseInt(tip.css("width").replace("px", "")) / 2) + "px",
           });
           tip.html(rangeValue)    
       }
   },
   convertColor: function(color){
       return $.convertColor(color).replace("=", "#")
   }
}

長い…

必須パラメータの指定

   vital: [
       "x", "y", "name",
   ],

まずはタグに必須のパラメータを指定していきます。
特に必須パラメータがなければこのプロパティは省略しても構いません。

今回は、スライダーの位置を指定する「x」「y」、スライダー固有の「name」パラメータを必須パラメータとして指定しました。

指定可能パラメータとその初期値

   pm: {
       name: "",
       x: "",
       y: "",
       width: "400",
       height: "4",
       min: "0",
       max: "100",
       step: "1",
       border: "0",
       border_color: "white",
       border_radius: "30",
       thumb_width: "30",
       thumb_height: "30",
       thumb_radius: "30",
       thumb_color: "white",
       thumb_border: "0",
       thumb_border_color: "white",
       thumb_img: "",
       base_color: "#999",
       active_color: "white",
       storage: "",
       target: "",
       exp: "",
       preexp: "",
       var: "",
       tip_width: "0",
       tip_height: "0",
       tip_color: "white",
       tip_radius: "0",
       tip_text_color: "black",
       tip_text_size: "24",
       tip_tail: "true",
       tip_pos: "top",
       tip_margin: "0",
   },

ぶっちゃけ、ここで記述していないパラメータでもタグで指定することは可能です。
ただその場合、初期値を指定できないし可読性も下がるので、できればちゃんと記述しておきたいところ

なお、パラメータで指定した値はすべて文字列として渡されるので、その点は注意しておきましょう。

スライダーを表示

    ...前略
   start: function(pm){
    ...後略

それではstart関数の解説です。
start関数は、前述したタグに指定できるパラメータと初期をセットしたオブジェクト「pm」を引数として持ちます。

これにより、タグで指定したパラメータがタグ内に渡されるわけですね。

変数名を値に変換

const variable = that.kag.embScript(_pm.var)

 ​この部分は、「var」パラメータ、つまりスライダーで変更する変数名からその値を取得しています。

スライダーのHTMLを作成

       const layer = TYRANO.kag.layer.getLayer("fix")
       let wrap = $('<div class="range fixlayer"></div>')
       let base = $('<div class="range_base"></div>')
       let active = $('<div class="range_active"></div>')
       let input = $('<input type=range>')
       let tip = $('<div class="range_tip"></div>')
       wrap.addClass(name)
       tip.addClass(name)

次の部分では、スライダー部分のHTMLを記述しています。

<div class="range fixlayer master">
    <div class="range_base" style="background: rgb(153, 153, 153); position: absolute; top: 223px; left: 85px; width: 300px; height: 2px; border: 0px solid white; border-radius: 0px;"></div>
    <div class="range_active" style="background: white; position: absolute; top: 223px; left: 85px; height: 2px; width: 300px; border-radius: 0px;"></div>
    <input type="range" name="master" min="0" max="100" step="1" id="input_master" value="100.00000149011612" class="event-setting-element" data-event-tag="slider" data-event-pm="{&quot;name&quot;:&quot;master&quot;,&quot;x&quot;:&quot;85&quot;,&quot;y&quot;:&quot;223&quot;,&quot;width&quot;:&quot;300&quot;,&quot;height&quot;:&quot;2&quot;,&quot;min&quot;:&quot;0&quot;,&quot;max&quot;:&quot;100&quot;,&quot;step&quot;:&quot;1&quot;,&quot;border&quot;:&quot;0&quot;,&quot;border_color&quot;:&quot;white&quot;,&quot;border_radius&quot;:&quot;0&quot;,&quot;thumb_width&quot;:&quot;24&quot;,&quot;thumb_height&quot;:&quot;24&quot;,&quot;thumb_radius&quot;:&quot;30&quot;,&quot;thumb_color&quot;:&quot;white&quot;,&quot;thumb_border&quot;:&quot;0&quot;,&quot;thumb_border_color&quot;:&quot;white&quot;,&quot;thumb_img&quot;:&quot;config/thumb_slider.png&quot;,&quot;base_color&quot;:&quot;#999&quot;,&quot;active_color&quot;:&quot;white&quot;,&quot;storage&quot;:&quot;&quot;,&quot;target&quot;:&quot;*master&quot;,&quot;exp&quot;:&quot;tf.master_exp()&quot;,&quot;preexp&quot;:&quot;&quot;,&quot;var&quot;:&quot;tf.current_master_vol&quot;,&quot;tip_width&quot;:&quot;72&quot;,&quot;tip_height&quot;:&quot;32&quot;,&quot;tip_color&quot;:&quot;=F2F2F2&quot;,&quot;tip_radius&quot;:&quot;0&quot;,&quot;tip_text_color&quot;:&quot;=333&quot;,&quot;tip_text_size&quot;:&quot;20&quot;,&quot;tip_tail&quot;:&quot;true&quot;,&quot;tip_pos&quot;:&quot;top&quot;,&quot;tip_margin&quot;:&quot;30&quot;,&quot;_tag&quot;:&quot;slider&quot;}" style="top: 223px; left: 85px; width: 300px; height: 2px; --thumb-width:24px; --thumb-height:24px; --thumb-img:url(\.\.\/\.\.\/\.\.\/image\/config\/thumb_slider\.png);">
    <div class="range_tip master" style="position: absolute; top: 161px; opacity: 0; z-index: 999; width: 72px; height: 32px; background: rgb(242, 242, 242); color: rgb(51, 51, 51); font-size: 20px; text-align: center; line-height: 32px; border-radius: 0px; --tip_width:72px; --tip_height:32px; --tip_color:#F2F2F2; --tip_tail:block; --tip_tail_top:72px solid =F2F2F2; --tip_tail_bottom:none; --tip_tail_arrow:16px; left: 349px;">100</div>
</div>

スライダーのHTML自体は↑のとおりなんですが、簡単に説明すると

・スライダー全体を包括する<div>
・スライダーの線部分のベースとなる<div>
・スライダーの線部分のうち、ツマミより左の部分となる<div>
・<input type=range>本体
・ツールチップとなる<div>

となります。
各HTML要素に指定されているclassとスクリプト中のclass指定を見比べてもらえれば、どこがどこに対応しているかわかると思います。

HTMLに各パラメータを適用

input.attr({
   name: name,
   min: _pm.min,
   max: _pm.max,
   step: _pm.step,
})
input.attr({
   id: "input_" + name,
   value: variable,
})
base.css({
   background: __slider_ui.convertColor(_pm.base_color),
   position: "absolute",
   top: _pm.y + "px",
   left: _pm.x + "px",
   width: _pm.width + "px",
   height: _pm.height + "px",
   "border": _pm.border + "px solid " +  __slider_ui.convertColor(_pm.border_color),
   "border-radius": _pm.border_radius + "px",
})
active.css({
   background: __slider_ui.convertColor(_pm.active_color),
   position: "absolute",
   top:  parseInt(_pm.border) + parseInt(_pm.y) + "px",
   left: parseInt(_pm.border) + parseInt(_pm.x) + "px",
   height: _pm.height + "px",
   width: _pm.width + "px",
   "border-radius": _pm.border_radius + "px",
})
if(_pm.thumb_img !== ""){
   input.css({
       top: _pm.y + "px",
       left: _pm.x + "px",
       width: _pm.width + "px",
       height: _pm.height + "px",
       "--thumb-width": _pm.thumb_width + "px",
       "--thumb-height": _pm.thumb_height + "px",
       "--thumb-img": `url(../../../image/${_pm.thumb_img})`,
   })    
}else{
   input.css({
       top: _pm.y + "px",
       left: _pm.x + "px",
       width: _pm.width + "px",
       height: _pm.height + "px",
       "--thumb-width": _pm.thumb_width + "px",
       "--thumb-height": _pm.thumb_height + "px",
       "--thumb-radius": _pm.thumb_radius == "0" ? "none" : _pm.thumb_radius + "px",
       "--thumb-color": __slider_ui.convertColor(_pm.thumb_color),
       "--thumb-border": _pm.thumb_border + "px solid " + __slider_ui.convertColor(_pm.thumb_border_color),
   })    
}

pm」オブジェクトとして渡されたタグのパラメータを適用していきます。

この辺は<input type=range>の属性とかCSSプロパティの話になってくるのであんまり説明することはない…

「"--thumb-width"」みたいに指定されている部分は↓の記事の「デザインカスタマイズ」の項を参照していただければ~

ツールチップの作成

       let _top = 0
       let _tail_top = "none"
       let _tail_bottom = "none"
       let _tail_arrow = ""
       let _tail = "none"
       if(_pm.tip_tail == "true"){
           _tail = "block"
       }
       if(_pm.tip_pos == "top"){
           _top = parseInt(_pm.y) - parseInt(_pm.tip_height) - parseInt(_pm.tip_margin) + "px"
           _tail_top = parseInt(_pm.tip_width) + "px solid " + _pm.tip_color
           _tail_arrow = parseInt(_pm.tip_height) / 2 * 1
       }else{
           _top = parseInt(_pm.y) + parseInt(_pm.tip_margin) + "px"
           _tail_bottom = parseInt(_pm.tip_width) + "px solid " + _pm.tip_color
           _tail_arrow = parseInt(_pm.tip_height) / 2 * -1
       }
       tip.css({
           position: "absolute",
           top: _top,
           opacity: 0,
           "z-index": "999",
           width: _pm.tip_width + "px",
           height: _pm.tip_height + "px",
           background: __slider_ui.convertColor(_pm.tip_color),
           color: __slider_ui.convertColor(_pm.tip_text_color),
           "font-size": _pm.tip_text_size + "px",
           "text-align": "center",
           "line-height": _pm.tip_height + "px",
           "border-radius": _pm.tip_radius + "px",
           "--tip_width": _pm.tip_width + "px",
           "--tip_height": _pm.tip_height + "px",
           "--tip_color": __slider_ui.convertColor(_pm.tip_color),
           "--tip_tail": _tail,
           "--tip_tail_top": _tail_top,
           "--tip_tail_bottom": _tail_bottom,
           "--tip_tail_arrow": _tail_arrow + "px"
       })

ツールチップとはこういうのです↓

画像3

つまみの上に現在の値を表示するやーつ

といっても、やってることは変わらず
HTMLを作成→HTML属性およびCSSプロパティを適用
なのでこちらも特に説明することは…ない…

ゲーム画面にスライダー要素を追加

       wrap.append(base).append(active).append(input)
       if(_pm.tip_width != "0"){
           wrap.append(tip)
       }
       layer.append(wrap)
       __slider_ui.updateRange(_pm.name, _pm.width)
       that.kag.event.addEventElement({
           tag: "slider",
           j_target: input,
           pm: pm,
       })
       this.setEvent(input, pm)
       that.kag.ftag.nextOrder()

最後、作成したHTML要素をゲーム画面に追加していきます。

ここで重要なのが「TYRANO.kag.event.addEventElement」と「this.setEvent」関数です。

addEventElement」で↓のようにtagプロパティにタグ名を指定することで、ロード時に該当のタグに対してsetEvent関数を実行するようになります。

that.kag.event.addEventElement({
   tag: "slider",
   j_target: input,
   pm: pm,
})

そしてstart関数内でsetEvent関数を実行することで、スライダーとしての各種イベント処理が定義されるというわけです。

それから最後の最後に「TYRANO.kag.ftag.nextOrder()」関数は必須です。この関数を実行することで初めて「次のタグに進む」処理が実行されます。
書き忘れると次のタグに進めずゲームが停止してしまうので注意しましょう。

スライダーのイベントを定義

それではスライダーを動かしたときの各種イベントを定義していきましょう。

setEvent関数の引数

...前略
   setEvent: function(input, pm){
...後略

[slider]タグのsetEvent関数は引数として「input」と「pm」を持ちます。
inputはスライダーの<input>要素、pmはタグで指定したパラメータです。

スライダーのツマミを動かしているときの処理

       //ツマミを動かしているとき
       input.on("input", function(e){
           $("." + _pm.name).find(".range_tip").css({
               opacity: 1,
           })
           __slider_ui.updateRange(_pm.name, _pm.width)
           that.kag.embScript(_pm.var + " = " + this.value)
           if(_pm.exp != ""){
               that.kag.embScript(_pm.exp, _pm.preexp)
           }
       })

まずはツマミを動かしている最中の処理(=inputイベント)です。

・該当のスライダーの「range_tip」クラスを持つ要素(=ツールチップ)の透明度を1に変更
updateRange関数を実行
・varパラメータで指定した変数に現在の値を代入
・expパラメータで指定したJS式を実行

やってることはこんな感じ
updateRange関数については後述しますが、スライダーを動かしているときの画面表示処理です。

スライダーのツマミを動かし終わったときの処理

       //ツマミを動かし終わったとき
       input.on("change", function(){
           $("." + _pm.name).find(".range_tip").css({
               opacity: 0,
           })
           //fixレイヤの場合はcallでスタックが積まれる
           if (_pm.storage != "" || _pm.target != "") {
               //コールスタックが帰ってきてない場合は、実行しないようにする必要がある
               //fixの場合はコールスタックに残る。
               var stack_pm = that.kag.getStack("call"); //最新のコールスタックを取得
               if(stack_pm == null){
                   //strong_stopの場合は反応しない
                   var _auto_next = _pm.auto_next;
                   if(that.kag.stat.is_strong_stop == true){
                       _auto_next = "stop";
                   }
                   //call実行
                   $("input").prop("disabled", true)
                   that.kag.ftag.startTag("call", {
                       storage: _pm.storage,
                       target: _pm.target,
                       auto_next: _auto_next
                   });
               }else{
                   //スタックが残っている場合
                   that.kag.log("callスタックが残っている場合、fixボタンは反応しません");
                   that.kag.log(stack_pm);
                   return false;
               }
           }
       })

今度は「ツマミを動かし終わったとき」の処理(=changeイベント)です。

・該当のスライダーの「range_tip」クラスを持つ要素(=ツールチップ)の透明度を0に変更
input要素を無効化
storageおよびtargetパラメータで指定した先をcall

やってることは割と単純ですね。
input要素の無効化ですが、これはcall先からreturnするときに有効化しています。詳しくは後述

マウスボタンを上げたときの処理

       input.on("mouseup", function(){
           $("." + _pm.name).find(".range_tip").css({
               opacity: 0,
           })
       })

クリックorドラッグが終了したときの処理というか…
ここでは単純に、ツールチップの透明度を0にしています。
ツールチップが確実に非表示になるようにするための処理ですね。

タグの登録

tyrano.plugin.kag.ftag.master_tag.slider = tyrano.plugin.kag.tag.slider
tyrano.plugin.kag.ftag.master_tag.slider.kag = tyrano.plugin.kag

これどう説明したらいいかわからないんですけど、ティラノのタグは「tyrano.plugin.kag.tag」内に追加するだけじゃなく、「tyrano.plugin.kag.ftag.master_tag」というオブジェクトにも追加してやらないといけないようです。

まあとりあえず、↑のように書いてもらえれば大丈夫です。

updateRange関数

const __slider_ui = {
   updateRange:  function(name, w){
       var rangeValue = $("#input_" + name).val();
       var active = w * ((rangeValue - $("#input_" + name).attr("min")) / ($("#input_" + name).attr("max") - $("#input_" + name).attr("min")));
       var param = {width: active + "px"}
       let left = $("#input_" + name).css("left").replace("px", "")
       $("." + name).find(".range_active").css(param);
       let tip = $("." + name).find(".range_tip")
       if(tip.length > 0){
           tip.css({
               left: parseInt(left) + parseInt(active) - (parseInt(tip.css("width").replace("px", "")) / 2) + "px",
           });
           tip.html(rangeValue)    
       }
   },
   convertColor: function(color){
       return $.convertColor(color).replace("=", "#")
   }
}

inputイベント中で実行していたupdateRange関数の内容です。

やってることとしては以下の通り

・ツマミの動きに合わせてアクティブ部分の長さを変える
・ツマミの位置に合わせてツールチップの表示位置を変える

あと「convertColor」って関数もありますが、これはティラノで「#ffffff」みたいな色指定をすると文字化け?みたいなことになるのでそれを回避するための処理です。

「switch」タグを追加

続いて[switch]タグの処理を見ていきます。

//スイッチ
tyrano.plugin.kag.tag.switch = {
   vital: [
       "x", "y", "name",
   ],
   pm: {
       name: "",
       x: "",
       y: "",
       width: "60",
       height: "4",
       border: "0",
       border_color: "white",
       border_radius: "30",
       thumb_width: "30",
       thumb_height: "30",
       thumb_radius: "30",
       thumb_color: "white",
       thumb_border: "0",
       thumb_border_color: "white",
       thumb_img: "",
       base_color: "#999",
       active_color: "white",
       storage: "",
       target: "",
       storage: "",
       target: "",
       var: ""
   },
   start: function(pm){
       const that = TYRANO
       const _pm = pm
       const name = _pm.name
       const variable = that.kag.embScript(_pm.var)
       const color = variable ? __slider_ui.convertColor(_pm.active_color) : __slider_ui.convertColor(_pm.base_color)
       const layer = TYRANO.kag.layer.getLayer("fix")
       let wrap = $('<div class="fixlayer"></div>')
       let input = $('<input type=checkbox />')
       let label = $('<label />')
       
       wrap.addClass(name)
       wrap.css({
           position: "absolute",
           top: _pm.y + "px",
           left: _pm.x + "px",
           width: _pm.width + "px",
           height: _pm.height + "px",
       })
       input.attr({
           id: "input_" + name,
       })
       label.attr({
           for: "input_" + name,
       })
       input.css({
           display: "none",
           opacity: 0,
           width: _pm.width + "px",
           height: _pm.width + "px",
       })
       if(_pm.thumb_img != ""){
           label.css({
               position: "absolute",
               cursor: "pointer",
               background: color,
               border: _pm.boder + "px solid " + __slider_ui.convertColor(_pm.border_color),
               "border-radius": _pm.border_radius == "0" ? "none" : _pm.border_radius + "px",
               width: _pm.width + "px",
               height: _pm.height + "px",
               display: "inline-block",
               transition: "0.2s",
               "box-sizing": "border-box",
               "--thumb_top": (_pm.thumb_height - _pm.height) / 2  * -1  - _pm.border/2 + "px",
               "--thumb-width": _pm.thumb_width + "px",
               "--thumb-height": _pm.thumb_height + "px",
               "--thumb-img": `url("../../../image/${_pm.thumb_img}")`,
               "--thumb-move": _pm.width / 2 + "px",
               "--active-color": __slider_ui.convertColor(_pm.active_color),
           })
       }else{
           label.css({
               position: "absolute",
               cursor: "pointer",
               background: color,
               border: _pm.boder + "px solid " + __slider_ui.convertColor(_pm.border_color),
               "border-radius": _pm.border_radius == "0" ? "none" : _pm.border_radius + "px",
               width: _pm.width + "px",
               height: _pm.height + "px",
               display: "inline-block",
               transition: "0.2s",
               "box-sizing": "border-box",
               "--thumb_top": (_pm.thumb_height - _pm.height) / 2  * -1  - _pm.border/2 + "px",
               "--thumb-width": _pm.thumb_width + "px",
               "--thumb-height": _pm.thumb_height + "px",
               "--thumb-radius": _pm.thumb_radius == "0" ? "none" : _pm.thumb_radius + "px",
               "--thumb-color": __slider_ui.convertColor(_pm.thumb_color),
               "--thumb-border": _pm.thumb_border + "px solid " + __slider_ui.convertColor(_pm.thumb_border_color),
               "--thumb-move": _pm.width / 2 + "px",
               "--active-color": __slider_ui.convertColor(_pm.active_color),
           })
       }
       input.prop("checked", variable)
   
       wrap.append(input)
       wrap.append(label)
       layer.append(wrap)
       that.kag.event.addEventElement({
           tag: "switch",
           j_target: input,
           pm: pm,
       })
       this.setEvent(input, label, pm)
       that.kag.ftag.nextOrder()
   },
   setEvent: function(input, label, pm){
       const that = TYRANO
       const _pm = pm
       input.on("change", function(){
           if(input.prop("checked")){
               that.kag.evalScript(_pm.var + " = true")
               label.css({
                   background: __slider_ui.convertColor(_pm.active_color)
               })
               //fixレイヤの場合はcallでスタックが積まれる
               if (_pm.storage != "" || _pm.target != "") {
                   //コールスタックが帰ってきてない場合は、実行しないようにする必要がある
                   //fixの場合はコールスタックに残る。
                   var stack_pm = that.kag.getStack("call"); //最新のコールスタックを取得
                   if(stack_pm == null){
                       //strong_stopの場合は反応しない
                       var _auto_next = _pm.auto_next;
                       if(that.kag.stat.is_strong_stop == true){
                           _auto_next = "stop";
                       }
                       //call実行
                       that.kag.ftag.startTag("call", {
                           storage: _pm.storage,
                           target: _pm.target,
                           auto_next: _auto_next
                       });
                   }else{
                       //スタックが残っている場合
                       that.kag.log("callスタックが残っている場合、fixボタンは反応しません");
                       that.kag.log(stack_pm);
                       return false;
                   }
               }
           }else{
               that.kag.evalScript(_pm.var + " = false")
               label.css({
                   background: __slider_ui.convertColor(_pm.base_color)
               })
               //fixレイヤの場合はcallでスタックが積まれる
               if (_pm.storage != "" || _pm.target != "") {
                   //コールスタックが帰ってきてない場合は、実行しないようにする必要がある
                   //fixの場合はコールスタックに残る。
                   var stack_pm = that.kag.getStack("call"); //最新のコールスタックを取得
                   if(stack_pm == null){
                       //strong_stopの場合は反応しない
                       var _auto_next = _pm.auto_next;
                       if(that.kag.stat.is_strong_stop == true){
                           _auto_next = "stop";
                       }
                       //call実行
                       that.kag.ftag.startTag("call", {
                           storage: _pm.storage,
                           target: _pm.target,
                           auto_next: _auto_next
                       });
                   }else{
                       //スタックが残っている場合
                       that.kag.log("callスタックが残っている場合、fixボタンは反応しません");
                       that.kag.log(stack_pm);
                       return false;
                   }
               }
           }
       })
   }
}
tyrano.plugin.kag.ftag.master_tag.switch = tyrano.plugin.kag.tag.switch
tyrano.plugin.kag.ftag.master_tag.switch.kag = tyrano.plugin.kag

やってることは[slider]タグと同じです。なのでstart関数、setEvent関数を中心に見ていきましょう。

スイッチを表示

スイッチのHTML要素はこんな感じになってます↓

<div class="fixlayer window" style="position: absolute; top: 212px; left: 1038px; width: 60px; height: 2px;">
    <input type="checkbox" id="input_window" class="event-setting-element" data-event-tag="switch" data-event-pm="{&quot;name&quot;:&quot;window&quot;,&quot;x&quot;:&quot;1038&quot;,&quot;y&quot;:&quot;212&quot;,&quot;width&quot;:&quot;60&quot;,&quot;height&quot;:&quot;2&quot;,&quot;border&quot;:&quot;0&quot;,&quot;border_color&quot;:&quot;white&quot;,&quot;border_radius&quot;:&quot;30&quot;,&quot;thumb_width&quot;:&quot;32&quot;,&quot;thumb_height&quot;:&quot;32&quot;,&quot;thumb_radius&quot;:&quot;30&quot;,&quot;thumb_color&quot;:&quot;white&quot;,&quot;thumb_border&quot;:&quot;0&quot;,&quot;thumb_border_color&quot;:&quot;white&quot;,&quot;thumb_img&quot;:&quot;config/thumb_switch.png&quot;,&quot;base_color&quot;:&quot;=F2F2F2&quot;,&quot;active_color&quot;:&quot;=F2F2F2&quot;,&quot;storage&quot;:&quot;&quot;,&quot;target&quot;:&quot;*window&quot;,&quot;var&quot;:&quot;tf.is_fullscreen&quot;,&quot;_tag&quot;:&quot;switch&quot;}" style="display: none; opacity: 0; width: 60px; height: 60px;">
    <label for="input_window" style="position: absolute; cursor: pointer; background: rgb(242, 242, 242); border-radius: 30px; width: 60px; height: 2px; display: inline-block; transition: all 0.2s ease 0s; box-sizing: border-box; --thumb_top:-15px; --thumb-width:32px; --thumb-height:32px; --thumb-img:url(&quot;../../../image/config/thumb_switch.png&quot;); --thumb-move:30px; --active-color:#F2F2F2;"></label>
</div>

input要素自体はチェックボックスで、非表示にしています。
input要素にlabel要素を紐付けることで、label要素をクリックでもinput要素の状態を変更できるようになっています。

ツマミの部分がないように見えますが、これはlabel要素内の「after」疑似要素を利用しています。

で、JSの記述はこう↓

       const layer = TYRANO.kag.layer.getLayer("fix")
       let wrap = $('<div class="fixlayer"></div>')
       let input = $('<input type=checkbox />')
       let label = $('<label />')
       
       wrap.addClass(name)
       wrap.css({
           position: "absolute",
           top: _pm.y + "px",
           left: _pm.x + "px",
           width: _pm.width + "px",
           height: _pm.height + "px",
       })
       input.attr({
           id: "input_" + name,
       })
       label.attr({
           for: "input_" + name,
       })
       input.css({
           display: "none",
           opacity: 0,
           width: _pm.width + "px",
           height: _pm.width + "px",
       })
       if(_pm.thumb_img != ""){
           label.css({
               position: "absolute",
               cursor: "pointer",
               background: color,
               border: _pm.boder + "px solid " + __slider_ui.convertColor(_pm.border_color),
               "border-radius": _pm.border_radius == "0" ? "none" : _pm.border_radius + "px",
               width: _pm.width + "px",
               height: _pm.height + "px",
               display: "inline-block",
               transition: "0.2s",
               "box-sizing": "border-box",
               "--thumb_top": (_pm.thumb_height - _pm.height) / 2  * -1  - _pm.border/2 + "px",
               "--thumb-width": _pm.thumb_width + "px",
               "--thumb-height": _pm.thumb_height + "px",
               "--thumb-img": `url("../../../image/${_pm.thumb_img}")`,
               "--thumb-move": _pm.width / 2 + "px",
               "--active-color": __slider_ui.convertColor(_pm.active_color),
           })
       }else{
           label.css({
               position: "absolute",
               cursor: "pointer",
               background: color,
               border: _pm.boder + "px solid " + __slider_ui.convertColor(_pm.border_color),
               "border-radius": _pm.border_radius == "0" ? "none" : _pm.border_radius + "px",
               width: _pm.width + "px",
               height: _pm.height + "px",
               display: "inline-block",
               transition: "0.2s",
               "box-sizing": "border-box",
               "--thumb_top": (_pm.thumb_height - _pm.height) / 2  * -1  - _pm.border/2 + "px",
               "--thumb-width": _pm.thumb_width + "px",
               "--thumb-height": _pm.thumb_height + "px",
               "--thumb-radius": _pm.thumb_radius == "0" ? "none" : _pm.thumb_radius + "px",
               "--thumb-color": __slider_ui.convertColor(_pm.thumb_color),
               "--thumb-border": _pm.thumb_border + "px solid " + __slider_ui.convertColor(_pm.thumb_border_color),
               "--thumb-move": _pm.width / 2 + "px",
               "--active-color": __slider_ui.convertColor(_pm.active_color),
           })
       }
       input.prop("checked", variable)

これはもうそんなに…説明することはない……

スイッチのイベントを定義

続いてsetEvent関数を見ていきましょう。

    setEvent: function(input, label, pm){
       const that = TYRANO
       const _pm = pm
       input.on("change", function(){
           if(input.prop("checked")){
               that.kag.evalScript(_pm.var + " = true")
               label.css({
                   background: __slider_ui.convertColor(_pm.active_color)
               })
               //fixレイヤの場合はcallでスタックが積まれる
               if (_pm.storage != "" || _pm.target != "") {
                   //コールスタックが帰ってきてない場合は、実行しないようにする必要がある
                   //fixの場合はコールスタックに残る。
                   var stack_pm = that.kag.getStack("call"); //最新のコールスタックを取得
                   if(stack_pm == null){
                       //strong_stopの場合は反応しない
                       var _auto_next = _pm.auto_next;
                       if(that.kag.stat.is_strong_stop == true){
                           _auto_next = "stop";
                       }
                       //call実行
                       that.kag.ftag.startTag("call", {
                           storage: _pm.storage,
                           target: _pm.target,
                           auto_next: _auto_next
                       });
                   }else{
                       //スタックが残っている場合
                       that.kag.log("callスタックが残っている場合、fixボタンは反応しません");
                       that.kag.log(stack_pm);
                       return false;
                   }
               }
           }else{
               that.kag.evalScript(_pm.var + " = false")
               label.css({
                   background: __slider_ui.convertColor(_pm.base_color)
               })
               //fixレイヤの場合はcallでスタックが積まれる
               if (_pm.storage != "" || _pm.target != "") {
                   //コールスタックが帰ってきてない場合は、実行しないようにする必要がある
                   //fixの場合はコールスタックに残る。
                   var stack_pm = that.kag.getStack("call"); //最新のコールスタックを取得
                   if(stack_pm == null){
                       //strong_stopの場合は反応しない
                       var _auto_next = _pm.auto_next;
                       if(that.kag.stat.is_strong_stop == true){
                           _auto_next = "stop";
                       }
                       //call実行
                       that.kag.ftag.startTag("call", {
                           storage: _pm.storage,
                           target: _pm.target,
                           auto_next: _auto_next
                       });
                   }else{
                       //スタックが残っている場合
                       that.kag.log("callスタックが残っている場合、fixボタンは反応しません");
                       that.kag.log(stack_pm);
                       return false;
                   }
               }
           }
       })
   }
   

今度は引数としてinputlabelpmを持っています。
やってることは[slider]タグとそれほど変わりません。

これで[slider][switch]のタグが追加されました。
タグの追加というと難しそうに聞こえるかもしれませんが、やってることは案外そうでもありません。

マクロ化するには複雑すぎる処理などはタグ化することで使いやすくなったりします。なによりタグはcondパラメータが使えるのでな…

わからないところなどありましたらコメントでご指摘お願いします。
へばな


サポートをしていただけると私がたいへんよろこびます。 ちなみに欲しい物リストはこちら→https://www.amazon.jp/hz/wishlist/ls/2DBRPE55L3SQC?ref_=wl_share