canvas で表示しているアニメーションの上に重なったカウンター、URL、画像のある場所では、 canvas にマウスイベントが届かないため、透明な別の canvas を更に上に配置し、そちらでマウスイベントを取得するようにして対処した。
しかし、今度は、アニメーション表示 canvas の上に重ねた URL 部分が反応しなくなった。透明な別 canvas の下にあるのだから当然と言えば当然だ。
で、調べた結果、やはり上に別の要素がある場合、下になった要素にはイベントが届かないことが分かった。また、設定で届くようにするような方法も用意されていないことも分かった。
とても困った仕様だが、そういう仕様なんだからしょうがない。
そこで、「複数枚重なったcanvasで透明領域にマウスイベントを見えている直下の要素に渡したい」を参考に、一番上の透明の canvas に届いたマウスイベント( click, mousemove, mouseover, mouseout )を、下にある要素に送るようにした。
これで URL をクリックした場合にリンク先に飛ぶ動作をするようにはなったが、マウスカーソルを URL 文字列の上にもっていっても色が変わるリンク文字列の動作をしてくれない。ということは、 mouseover, mouseout を送っても、色を変える動作はしないということだ。おそらく、イベントの種別として、 MouseEvent ではなく UIEvent を送る必要があるのだと思う。そして、 URL 文字列の要素に対し、 mouseover のイベントが届くタイミングで UIEvent の DOMFocusIn を送り、 mouseout のイベントが届くタイミングで UIEvent の DOMFocusOut を送ってやることでリンク文字列の色を変える動作をすると思われる。
しかし、問題が2つある。
まず1つ目の問題は、 UIEvent を送るタイミングをどのように決めるかである。上にある canvas 上で mousemove, mouseover, mouseout のイベントが届いたときに elementFromPoint で、マウスカーソル位置にある下の要素を取得しているが、いつその下の要素の上にマウスカーソルが入り、いつ出たのかを決定することができないからだ。この問題は、elementFromPoint で発見した要素を currentItem に追加し同時にその要素に UIEvent の DOMFocusIn を送り、その後の elementFromPoint で、 currentItem が示す要素と異なる要素または null となった場合に、 UIEvent の DOMFocusOut を送ることで、なんとかなるかも知れない。
しかし2つ目の問題として、 UIEvent を発行し DOMFocusIn を下の要素に送った場合、その要素にフォーカスが変わるため、上にあるイベントを受け取っていた要素がイベントを受け取れなくなってしまうであろうことがあげられる。こちらの問題の方が致命的で、本来イベントを受け取るべき要素がイベントを受け取れなくなるということなので、 URL のリンク文字列の色を変えるためにイベントを送るという考え方自体がダメということである。
そこで考え方を変え、 mousemove の elementFromPoint で発見した要素がリンク文字列であれば要素を currentItem にセットしその要素の色をマウスホバー色に変え、その後の elementFromPoint で、 currentItem が示す要素と異なる要素または null となった場合に、その要素の色を戻すことで対応した。
下が完成版。
var currentItem = null;
var currentItemCol = 0;
function passmouseevent(e, type)
{
var mev;
var tmp;
var under;
tmp = event1.style.display;
event1.style.display = "none";
under = document.elementFromPoint(e.clientX, e.clientY);
if (type == "mousemove") {
if (under != null) {
if (currentItem != under) {
if ((currentItem != null) && (currentItem.tagName == "A")) {
currentItem.style.color = currentItemCol;
}
currentItem = under;
currentItemCol = under.style.color;
if (under.tagName == "A") {
under.style.color = "#ff00ff";
}
}
} else {
if ((currentItem != null) && (currentItem.tagName == "A")) {
currentItem.style.color = currentItemCol;
currentItem = null;
}
}
}
if (under != null) {
mev = document.createEvent('MouseEvents');
mev.initMouseEvent(e.type,
e.bubbles,
e.cancelable,
e.view,
e.detail,
e.screenX,
e.screenY,
e.clientX,
e.clientY,
e.ctrlKey,
e.altKey,
e.shiftKey,
e.metaKey,
e.button,
e.relatedTarget);
under.dispatchEvent(mev);
}
event1.style.display = tmp;
}
function testclick(e)
{
passmouseevent(e, 'click');
}
function testmove(e)
{
passmouseevent(e, 'mousemove');
}
今回やりたいことを始めてから完成までの経緯。
(1)トップページにアニメーションを表示したいな。
(2)バナー部分の、カウンターやURLリンクや画像の下に背景的に表示したいな。
(3)アニメーションはマウスカーソルにも反応してほしいな。
(4)あれ?カウンターやURLリンクや画像のところにマウスカーソルが来ると反応しないな。
(5)透明な canvas を一番上に用意して、そこでイベントを取ろう。
(6)あれ?URLリンクに反応しないな。
(7)要素の下の要素にもイベントを送る必要があるな。
(8)あれ?URLリンクの色がマウスカーソルホバー時に変わらないな。
(9)どうやらイベントを送っても変わってくれないらしい。
(10)イベントを送るタイミングを考えて、そのタイミングで代わりに直接色を変えよう。
やりたかったことは、ちょっとアニメーションを表示したかっただけなのだが、実現するために結局 javascript で相当難しい処理を実装する羽目になった。アニメーションの処理自体も javascript でゴリゴリ書かないと出来なかった。
この程度のことでこんな作業が必要なようでは、到底「HTML5 の登場でアニメーションが簡単にできるようになった」という状況ではないのではないか。アニメーションは Adobe Animate を使うとしても、今回のようなちょっとしたレイアウト上の希望で、いちいち javascript で複雑なコードをゴリゴリ書かないといけないというのは、ちょっといただけない。(ちなみに、以前は普通にパッケージとして購入できていた Adobe の製品だが、今では全て月額使用料を支払い続けなければ使用できない・・・。とてもじゃないが、もう Adobe の製品は購入できない。というか、月額で使用料が発生し続けるものって「購入」と呼べるのだろうか?)