jQuery実践ガイド:スライドパズル、固定サイドバー、Googleマップアプリの構築

jQueryの基本概念

jQueryは、DOM要素の選択と操作を簡素化する強力なライブラリである。例えば、id属性がtargetElの要素を選択するには、以下のように記述する。

const $element = $("#targetEl");

選択された要素はjQueryオブジェクトとしてラップされる。メソッドの多くは引数の有無によってゲッターとセッターの両方として機能する。幅を取得する場合はwidth()を引数なしで呼び出し、設定する場合は数値を渡す。

// 幅の取得
const currentWidth = $element.width();
// 幅の設定
$element.width(500);

スライドパズルの実装

jQuery UIのdraggableを活用し、画像を分割したピースをスライドさせるパズルを構築する。

ピースの生成と配置

元画像をグリッド状に分割し、各ピースをdiv要素として生成する。背景画像をずらすことで、一つの画像を複数のピースに見せかける。

const GRID_COL = 3;
const GRID_ROW = 4;
const $puzzleContainer = $("#puzzle-board");
const $sourceImg = $puzzleContainer.find("img");
const imgSrc = $sourceImg.attr("src");
const pieceWidth = Math.floor($sourceImg.width() / GRID_COL);
const pieceHeight = Math.floor($sourceImg.height() / GRID_ROW);

$sourceImg.remove();

for (let r = 0; r < GRID_ROW; r++) {
    for (let c = 0; c < GRID_COL; c++) {
        const offsetX = pieceWidth * c;
        const offsetY = pieceHeight * r;
        
        $("<div/>").css({
            width: pieceWidth,
            height: pieceHeight,
            position: "absolute",
            left: offsetX,
            top: offsetY,
            backgroundImage: `url(${imgSrc})`,
            backgroundPosition: `-${offsetX}px -${offsetY}px`
        }).appendTo($puzzleContainer);
    }
}

シャッフルとドラッグ制御

ピースをランダムに配置するためのFisher-Yatesアルゴリズムを実装し、空白スペースに隣接するピースのみが移動できるようにdraggableの挙動を制御する。

function randomizeItems(arr) {
    for (let i = arr.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [arr[i], arr[j]] = [arr[j], arr[i]];
    }
    return arr;
}

let $pieces = $puzzleContainer.children();
randomizeItems($pieces);

$pieces.draggable({
    containment: "parent",
    grid: [pieceWidth, pieceHeight],
    start: function(e, ui) {
        // ドラッグ開始時の制御ロジック
        // 空白スペースと同じ行または列か判定し、軸を制限する
    }
});

クリア判定とタイム記録

ピースが全て正しい位置に移動したかを確認し、クリア時にかかった時間をlocalStorageに保存する。

function checkClearState() {
    let matchedCount = 0;
    $pieces.each(function(index) {
        const $el = $(this);
        const currentPos = {
            top: parseInt($el.css("top")),
            left: parseInt($el.css("left"))
        };
        if (currentPos.top === correctPositions[index].top && currentPos.left === correctPositions[index].left) {
            matchedCount++;
        }
    });

    if (matchedCount === $pieces.length) {
        clearInterval(gameTimer);
        const totalTime = (elapsedTime.hours * 3600) + (elapsedTime.minutes * 60) + elapsedTime.seconds;
        
        const storedBest = localStorage.getItem("puzzleBest");
        if (!storedBest || totalTime < parseInt(storedBest)) {
            localStorage.setItem("puzzleBest", totalTime);
            $("<p/>", { text: "新しいベストタイムです!" }).appendTo("#game-ui");
        }
    }
}

スクロール追従型サイドバーの構築

CSSのposition: fixedはドキュメント全体を基準とするため、レイアウトによっては崩れが生じる。jQueryを用いてスクロール量を監視し、特定の位置までは静的に、超えた場合は固定として振る舞わせることで解決する。

位置の固定とリサイズ対応

const $navAside = $("aside");
const initialOffset = $navAside.offset();
const $windowObj = $(window);

$windowObj.on("scroll", function() {
    if ($windowObj.scrollTop() > initialOffset.top) {
        $navAside.css({
            position: "fixed",
            top: 0,
            left: initialOffset.left
        });
    } else {
        $navAside.css({
            position: "static"
        });
    }
});

$windowObj.on("resize", function() {
    // リサイズ時にleft位置などを再計算
    const wrapperLeft = $(".wrapper").offset().left;
    $navAside.css("left", wrapperLeft);
});

スムーズスクロールとハッシュ制御

ナビゲーションリンクのクリック時に、アニメーションで対象セクションまでスクロールさせる。また、hashchangeイベントを監視し、ブラウザの戻るボタンにも対応させる。

function animateScrollTo(targetOffset, hashVal) {
    $("html, body").animate({ scrollTop: targetOffset }, 400, function() {
        if (hashVal) window.location.hash = hashVal;
    });
}

$navAside.on("click", "a", function(e) {
    e.preventDefault();
    const targetId = $(this).attr("href");
    const targetTop = $(targetId).offset().top;
    animateScrollTo(targetTop, targetId);
});

$windowObj.on("hashchange", function() {
    const currentHash = window.location.hash;
    if (currentHash) {
        const targetTop = $(currentHash).offset().top;
        animateScrollTo(targetTop, currentHash);
    }
});

インタラクティブなGoogleマップの作成

Google Maps APIとjQueryを組み合わせ、マーカーの配置、住所の逆ジオコーディング、距離計算を行うアプリケーションを構築する。

地図の初期化とカスタムオーバーレイ

地図を描画し、会社の本社などを示すカスタムマーカーと情報ウィンドウを追加する。

const gMaps = google.maps;
const mapCenter = new gMaps.LatLng(35.681236, 139.767125);
const mapConfig = {
    zoom: 14,
    center: mapCenter,
    mapTypeId: gMaps.MapTypeId.ROADMAP
};
const mapInstance = new gMaps.Map(document.getElementById("map-canvas"), mapConfig);

const hqMarker = new gMaps.Marker({
    position: mapCenter,
    map: mapInstance,
    icon: "img/hq_icon.png"
});

const infoPanel = new gMaps.InfoWindow({
    content: document.getElementById("hq-details")
});

gMaps.event.addListener(hqMarker, "click", function() {
    infoPanel.open(mapInstance, hqMarker);
});

地点の追加と住所の取得

地図のクリックイベントからマーカーを追加し、ジオコーダーで住所を取得してUIを更新する。

let clickCounter = 0;
const geoCoder = new gMaps.Geocoder();

gMaps.event.addListener(mapInstance, "click", function(event) {
    if (clickCounter >= 2) return;
    
    const markerOpts = {
        map: mapInstance,
        position: event.latLng,
        draggable: true
    };
    
    const newMarker = new gMaps.Marker(markerOpts);
    
    geoCoder.geocode({ latLng: event.latLng }, function(results) {
        const address = results[0].formatted_address;
        // jQueryでUIに住所を表示
    });
    
    clickCounter++;
});

距離と料金の計算

距離行列サービスを利用して2点間の移動距離を算出し、料金を計算して表示する。

const distService = new gMaps.DistanceMatrixService();
distService.getDistanceMatrix({
    origins: [startLatLng],
    destinations: [endLatLng],
    travelMode: gMaps.TravelMode.DRIVING
}, function(response) {
    const distText = response.rows[0].elements[0].distance.text;
    const distVal = parseFloat(distText);
    // 距離と重量から料金を算出しUIに反映
});

タグ: jQuery jQuery UI Google Maps API localstorage javascript

7月19日 21:31 投稿