クリックした場所を「等倍表示」する

「Lightbox」の最初の表示は「ウインドウサイズ」です。 これは画像の全体が表示さ

れるので、全体画像の任意の場所をクリックすると、その箇所を中心に「等倍表示」できれば、スクロールの必要が減ってとても俊敏に操作が出来ます。

 

PHOTOHITOの元データ画像の表示画面にはこの機能は無く、Photoshopなどでこの機能を知っていると、扱い難いなと思います。 今回はこの機能に着手しました。

 

当初、位置計算コードが複雑になり、誤差が出そうだと思われましたが、やってみると充分に使える機能になりました。「ウインドウサイズ」表示は、周囲に1%の余裕の枠を作っているので、最初はこれを補正したコードを作りました。 その後で余裕分を無視したコードを作って比較しましたが、全く違いが判りません。

 

採用したのは当然簡単な方の位置計算コードです。 説明はとても長くなるので省略します。 図を書いて、少しずつ詰めただけです。「object-fit」を使っているので、画像とコンテナ要素で縦横比が一致する境界の前後で、計算式が異なる点が肝です。

 

let actal_x; // Actual Pixels表示スクロールx値
let actal_y; // Actual Pixels表示スクロールy値

function mag(event){
    let lightbox=document.querySelector('#lightbox');
    let box_img=document.querySelector('#box_img');
    let nwidth=box_img.naturalWidth;
    let nhight=box_img.naturalHeight;
    let ratio=nwidth/nhight
    let top=event.offsetY;
    let left=event.offsetX;
    let ww=lightbox.clientWidth;
    let wh=lightbox.clientHeight;

    if(ww<wh*ratio){
        let yp=ratio*(top - wh/2)/ww +1/2;
        actal_x=(nwidth*left)/ww - ww/2;
        actal_y=nhight*yp - wh/2; }
    else{
        let xp=((left - ww/2)/wh)/ratio +1/2;
        actal_x=nwidth*xp - ww/2;
        actal_y=(nhight*top)/wh - wh/2; }}

 

今回のバージョンで試すと判りますが、クリックした画像の場所が、かなり正確に画面の中央に拡大されます。 ただし、画像の周辺部の拡大は、それ以上スクロール出来ないので少しズレる事がありますが。

 

 

 

「Lightbox」の操作仕様の変更 

クリックポイントを「等倍表示」する操作を採り入れたので、大幅に操作仕様を変更しました。「ウインドウザイズ」で画像を開くまでは同じです。

 

●「ウインドウザイズ」の画像で「等倍表示」したい場所を「左Click」すると、その場所を中心にした「等倍表示」に移行します。

 

●「等倍表示」の「Lightbox」内の任意の場所の「左Click」で「ウインドウザイズ」の表示戻る。

 

以降、「等倍表示」⇄「ウインドウザイズ」を繰り返す。

 

 

 

 

「Lightbox」を終了する方法 

「等倍表示」「ウインドウザイズ」のどちらの画面でも、マウスポインターを上部に移動すると「✖」マークが出ます。

 

●「✖」マークを「左Click」すると「Lightbox」を終了し「ユーザー別写真リスト」の画面に戻ります。

 

●「等倍表示」「ウインドウザイズ」のどちらの画面でも、「ESC」キーを押す事でも、「ユーザー別写真リスト」の画面に戻れます。

 

 

 

この「Lightbox」を終了するボタンや「ESC」キーの操作は、「Chorme」の全画面表示の操作仕様を意図的に真似ました。 下は、全画面表示で「Lightbox」のスイッチを表示したところで、文字の太い方が「PhotoHito Viewer」のスイッチです。

 

 

紛らわしい様に見えまず、実際は操作に困るという事はありません。 デザインと操作を似せたので、だれでもすぐ理解できると思います。

 

 

 

「Exifデータ」を「Lightbox」内で参照可能に 

●「等倍表示」「ウインドウザイズ」のどちらの画面でも「Space」キーを押すと、画面の右横に「Exifパネル」が表示されます。

 

 

▪ もういちど「Space」キーを押すと「Exifパネル」は非表示になります。

 

▪「Exifパネル」の「Lightbox」内の表示は、「Lightbox」を閉じると非表示にリセットされ、「ユーザー別写真リスト」での上部表示の設定とは独立しています。

 

 

 

ツールの適用画面の設定を修正 

「 https://photohito.com/photo/* 」をツール適用先に設定していましたが、この「/photo/」のパスは、画像投稿等の画面も同じなので、それらのページのデザインを崩していました。

 

「Tampermonkey」の「@match指定」は細かな指定が出来ないので、「/photo/」のパスでの適用範囲を、以下のコードで選択する様に変更しました。

 

if(path.match(/\/photo\/[0-9]{8}\/$/)){ ……… }

 

これは正規表現を使い、「/photo/12345678/」といった 8桁数のみの「ユーザー別写真リスト」の画面にだけマッチします。 これで、誤適用はなくなると思います。

 

 

 

「PhotoHito Viewer」の扱い方 

基本的な扱い方は、下のページを参照ください。 ただし、バージョンごとに仕様を変更しているので、説明が一致しない点はツールの制作記事を参照ください。

 

 

 

 

「PhotoHito Viewer」を利用するには

このツールは Chrome / Edge / Firefox版の拡張機能「Tampermonkey」上で動作します。 以下に、このツールの導入手順を簡単に説明します。

 

❶「Tampermonkey」を導入します

◎ 使用しているブラウザに拡張機能「Tampermonkey」を導入する事が必要です。

既に「Tampermonkey」を導入している場合は、この手順 ❶ は不要です。 

拡張機能の導入については、以下のページに簡単な説明があるので参照ください。

 

 

❷「Tampermonkey」にスクリプトを登録します

◎「Tampermonkey」の「」マークの「新規スクリプト」タブを開きます。

 

 

 

◎「新規スクリプト」には、最初からテンプレートが記入されています。 これは全て削除して、完全に空白の編集枠に 下のコードをコピー&ペーストします。

 

〔コピー方法〕 軽量シンプルなツール「PreBox Button   」を使うと

  コード枠内を「Ctrl+左Click」➔「Copy code 」を「左Click」

  の操作で、掲載コードのコピーが可能になります。

 

◎ 最後に「ファイル」メニューの「保存」を押すと、ツールが使用可能になります。

 

 

〔 PhotoHito Viewer 〕 ver. 0.6

 

// ==UserScript==
// @name         PhotoHito Viewer
// @namespace    http://tampermonkey.net/
// @version      0.6
// @description 写真投稿サイトPHOTOHITO専用の画像検証ビューア
// @author       PHOTOHITO User
// @match        https://photohito.com/photo/*
// @match        https://photohito.com/lens/*
// @match        https://photohito.com/camera/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=photohito.com
// @run-at       document-start
// @grant        none
// ==/UserScript==



let path=location.pathname;
if(path.match(/\/photo\/[0-9]{8}\/$/) || path.startsWith('/lens') || path.startsWith('/camera')){

    let retry=0;
    let interval=setInterval(wait_target, 10);
    function wait_target(){
        retry++;
        if(retry>10){ // リトライ制限 100回 0.1secまで
            clearInterval(interval); }
        let target=document.body; // 監視 target
        if(target){
            clearInterval(interval);
            list_env(); }}


    function list_env(){
        let style=
            '<style id="ph_v">'+
            'h1#logo { display: none; } '+
            'body .wrapper_full article #path { margin: 2px 15px 15px !important; } '+
            '#user_header { margin: -14px 15px 0; } '+
            'body .wrapper article h1 { font-size: 120%; padding: 0 0 12px 40px; cursor: pointer; } '+
            'body .wrapper article h1 i { margin-top: -18px; } '+
            '#photo_detail .wrapper article h1 { cursor: auto; } '+
            'body .wrapper_full article section { margin-right: 15px !important; } '+
            'body .wrapper_full aside { position: absolute; top: 184px; right: 0; '+
            'padding: 10px 20px; background: #fff; z-index: 10; display: none; } '+
            'body .wrapper_full aside .notlogin_banner, '+
            'body .wrapper_full aside .side_ad { display: none; } '+
            'section { display: flex; flex-direction: column; } '+
            '.wrapper article h1 { order: -2; } '+
            '#photo_list_search_form { width: 600px; order: -2; } '+
            '#photo_list_sort {width: 320px; position: relative; top: -50px; left: 600px; order: -2; } '+
            '.photo_list { order: 0; } '+
            '#pagenate { padding: 7px 0; order: -1; margin: -50px 0 20px; background: #f8f8fa; } '+
            '#photo_detail #photo_view { overflow: hidden; } '+
            '#photo_detail #photo_view a { margin: 0 auto !important; width: fit-content; } '+
            '#photo_detail #photo_view a:hover { box-shadow: 0 100vw 0 100vw #000; } '+
            '.adBox-300-2, .ad_detail, .content_ad, '+
            '#div-gpt-ad-p-300x250, #div-gpt-ad-p-side_300x250 { display: none; }'+
            '</style>';

        if(!document.querySelector('#ph_v')){
            document.documentElement.insertAdjacentHTML('beforeend', style); }}

} // 共通CSS




window.addEventListener('DOMContentLoaded', function(){

    let path=location.pathname;
    if(path.match(/\/photo\/[0-9]{8}\/$/)){ //「ユーザー別写真リスト」の画面

        box_env();
        exif_adupt();



        let photo_side=document.querySelector('#photo_view');
        if(photo_side){
            photo_side.onclick=function(){ // 画像両サイドのクリックでウインドウを閉じる
                window.close(); }}



        function box_env(){
            let lightbox=
                '<div id="lightbox">'+
                '<div id="photo_sw"><p id="photo_link">✖</p></div>'+
                '<img id="box_img">'+
                '<style>'+
                '@keyframes fadeIn { 0% {opacity: 0} 100% {opacity: 1}} '+
                '.fin { animation: fadeIn .5s ease 0s 1 normal; animation-fill-mode: both; } '+
                '@keyframes fadeOut { 0% {opacity: 1} 100% {opacity: 0}} '+
                '.fout { animation: fadeOut .2s ease 0s 1 normal; animation-fill-mode: both; } '+
                '#lightbox { position: fixed; top: 0; left: 0; z-index: 3000; '+
                'display: grid; place-items: center; '+
                'background: #000; width: 100vw; height: 100vh; overflow: auto; visibility: hidden; } '+
                '#photo_sw { position: fixed; top: 0; width: 100%; height: 15%; } '+
                '#photo_sw:hover #photo_link { opacity: 1; } '+
                '#photo_link { position: absolute; top: 20px; left: calc(50% - 24px); height: 34px; '+
                'font: 22px/24px Meiryo; padding: 13px 15px 0; border-radius: 40px; '+
                'background: rgba(68, 68, 68, 0.7); color: #fff; cursor: pointer; opacity: 0; } '+
                '#box_img { width: 98vw; height: 98vh; padding: 1vh 1vw; object-fit: contain; } '+
                '</style></div>'+
                '<div id="exif_sw">'+
                'Exifを上部表示'+
                '<style>'+
                '#exif_sw { position: absolute; top: 84px; right: 610px; font-size: 15px; '+
                'padding: 11px 10px 8px; border: solid 1px #999; border-radius: 2px; '+
                'background: #fff; cursor: pointer; }'+
                '</style></div>';

            if(!document.querySelector('#lightbox')){
                document.body.insertAdjacentHTML('beforeend', lightbox); }}



        function exif_env(){
            let ex_style=
                '<style id="exif_style">'+
                '#photo_detail .wrapper:nth-child(3) { width: 1000px !important; } '+
                '#photo_detail #photo_view a { margin: 0 260px 0 0 !important; width: fit-content; } '+
                '#photo_detail #photo_view { display: flex; flex-wrap: wrap; justify-content: center; } '+
                '#photo_detail #photo_view h1 { width: 100%; } '+
                '#sideInfo { position: absolute;top: 94px; left: calc(50vw + 220px); '+
                'padding: 6px 8px 0; width: 250px; background: #fff; } '+
                '#sideInfo.active { top: 0; z-index: 4000; } '+
                '.notlogin_banner, #tag_area, #location_area, #btn_violation, #urlpaste_area { '+
                'display: none; } '+
                '#photo_view_side { display: none; } '+
                'aside table { margin: 5px 0 0; } '+
                'aside tr { display: flex; flex-direction: column; } '+
                'aside th { padding: 0; font-size: 12px; } '+
                'aside td { padding: 0 0 5px; text-align: left; }'+
                '</style>';

            if(!document.querySelector('#exif_style')){
                document.documentElement.insertAdjacentHTML('beforeend', ex_style); }}



        let exif_sw=document.querySelector('#exif_sw'); // Exifパネルの配置スイッチ
        if(exif_sw){
            exif_sw.onclick=function(){
                let exif_style=document.querySelector('#exif_style');
                if(exif_style){
                    document.cookie='PHView_e=0; path=/; Max-Age=2592000'; // 30日有効
                    exif_style.remove(); }
                else{
                    document.cookie='PHView_e=1; path=/; Max-Age=2592000'; // 30日有効
                    exif_env(); }}}



        function exif_adupt(){
            if(get_cookie('PHView_e')==1){
                exif_env(); }
            else{
                let exif_style=document.querySelector('#exif_style');
                if(exif_style){
                    exif_style.remove(); }}

            let sideInfo=document.querySelector('#sideInfo');
            if(sideInfo && sideInfo.classList.contains('active')){
                sideInfo.classList.remove('active'); }}



        let img_link=document.querySelector('#photo_view a');
        let l_img=document.querySelector('#photo_view img:last-child');
        if(img_link && l_img){
            img_link.addEventListener('mousedown', function(event){
                if(!event.shiftKey && !event.ctrlKey){
                    event.preventDefault();
                    set_img(l_img);
                    org_size(); }}); }



        function set_img(target){
            let html_=document.querySelector('html');
            let lightbox=document.querySelector('#lightbox');
            let box_img=lightbox.querySelector('#box_img');
            if(lightbox && box_img){
                let img_url=target.getAttribute('src');
                if(img_url){
                    let last=img_url.slice(-5);
                    if(last=='l.jpg'){
                        let new_url=img_url.slice(0, -5) +'o.jpg';
                        box_img.src=new_url;
                        html_.style.overflow='hidden';
                        lightbox.style.visibility='visible';
                        lightbox.classList.remove('fout');
                        lightbox.classList.add('fin'); }}}}



        function org_size(){
            let lightbox=document.querySelector('#lightbox');
            let box_img=lightbox.querySelector('#box_img');
            let actal_x; // Actual Pixels表示スクロールx値
            let actal_y; // Actual Pixels表示スクロールy値

            lightbox.onclick=function(event){
                event.stopImmediatePropagation();
                if(!event.ctrlKey){
                    if(box_img.style.width!='auto'){
                        mag(event);
                        let org='width: auto; height: auto; padding: 0;';
                        box_img.setAttribute('style', org);
                        lightbox.scrollTo(actal_x, actal_y); }
                    else{
                        let nor='width: 98vw; height: 98vh; padding: 1vh 1vw;';
                        box_img.setAttribute('style', nor); }}
                else{
                    close(); }


                function mag(event){
                    let lightbox=document.querySelector('#lightbox');
                    let box_img=document.querySelector('#box_img');
                    let nwidth=box_img.naturalWidth;
                    let nhight=box_img.naturalHeight;
                    let ratio=nwidth/nhight
                    let top=event.offsetY;
                    let left=event.offsetX;
                    let ww=lightbox.clientWidth;
                    let wh=lightbox.clientHeight;

                    if(ww<wh*ratio){
                        let yp=ratio*(top - wh/2)/ww +1/2;
                        actal_x=(nwidth*left)/ww - ww/2;
                        actal_y=nhight*yp - wh/2; }
                    else{
                        let xp=((left - ww/2)/wh)/ratio +1/2;
                        actal_x=nwidth*xp - ww/2;
                        actal_y=(nhight*top)/wh - wh/2; }}}

        } //org_size()



        let photo_link=document.querySelector('#photo_link');
        if(photo_link){
            photo_link.onclick=function(event){
                event.stopImmediatePropagation();
                close(); }}



        document.addEventListener('keydown', function(event){
            if(event.keyCode==27){
                close(); }
            else if(event.keyCode==32){
                event.preventDefault();
                show_exif(); }});



        function close(){
            let html_=document.querySelector('html');
            let lightbox=document.querySelector('#lightbox');
            if(lightbox){
                html_.style.overflow='inherit';
                lightbox.classList.remove('fin');
                lightbox.classList.add('fout');
                exif_adupt();
                setTimeout(()=>{
                    lightbox.style.visibility='hidden';
                }, 200); }}



        function show_exif(){
            let lightbox=document.querySelector('#lightbox');
            let sideInfo=document.querySelector('#sideInfo');
            if(sideInfo && lightbox && lightbox.style.visibility=='visible'){
                exif_env();
                if(!sideInfo.classList.contains('active')){
                    sideInfo.classList.add('active'); }
                else{
                    sideInfo.classList.remove('active'); }}}



        function get_cookie(name){
            let cookie_req=document.cookie.split('; ').find(row=>row.startsWith(name));
            if(cookie_req){
                if(cookie_req.split('=')[1]==null){
                    return 0; }
                else{
                    return cookie_req.split('=')[1]; }}
            if(!cookie_req){
                return 0; }}


    } //「ユーザー別写真リスト」の画面




    if(path.startsWith('/lens') || path.startsWith('/camera')){ //「サムネイルリスト」の画面

        let plist_a=document.querySelectorAll('.photo_list .grid > a');
        for(let k=0; k<plist_a.length; k++){
            plist_a[k].setAttribute('target', '_blank'); }

        let title=document.querySelector('.wrapper article h1');
        let aside=document.querySelector('.wrapper_full aside');
        if(title && aside){
            title.onclick=function(){
                if(aside.style.display!='block'){
                    aside.style.display='block'; }
                else{
                    aside.style.display='none'; }}}

    } //「lens」「camera」の検索抽出画像の「サムネイルリスト」の画面

}); // main()



 

 

 

「PhotoHito Viewer」最新版について 

旧いバージョンの JavaScriptツールは、アメーバのページ構成の変更で動作しない場合があり、導入する場合は最新バージョンをお勧めします。

 

●「PhotoHito Viewer」の最新バージョンへのリンクは、以下のページのリンクリストから探せます。