見出し画像

アナログ時計をAIにデザインさせることは可能なのか?ーGoogleサイトで作るグループウェア(176)ー

🙇🏻いつも、Googleサイトで作るグループウェアを見ていただき、ありがとうございます!


この記事を読んで欲しい方

企業DXや校務DXの進め方に悩んでいる方
クラウドアプリの導入に悩んでいる方
自分だけのGoogleサイトを作ってみたい方

①アナログ時計デザイン

 みなさんこんにちは。
 現在公開しているアナログ時計ジェネレータLiteでは、人の力でいろいろなデザインができますが、さて、これをAIにデザインさせることって可能なのでしょうか?
 実は、現在公開してはいませんが、アナログ時計ジェネレータProを作成しており、その機能の中に、設定項目をJSON形式で吐き出す機能があります。
 このJSONをAIに読み込ませ、デザイン内容をプロンプトで指示することで、ある程度AIにデザインさせることに成功したので、報告します。


②アナログ時計ジェネレータPro(非公開)の機能

 アナログ時計ジェネレータPro版は、公開中のLite版にいくつかの拡張を施しています。
①枠線と文字盤に材質の設定が可能:
 材質は光沢(標準)、メタリック、パール、サテンの4種類から選択
②時計の針の種類の増加:
 Lite版の直線、三角形の2種類に加え、矢印、ダイアモンド、葉型、5角形、ペンシル針、ブレゲ針、スペード針、長方形、台形と全11種類から選択
③針の尾の表示:
 針の尾の部分も表示し、長さを指定可能
④デザインの設定をJSON形式で保存・読込:
 設定を保存することで、いつでもデザインした時計を再現することが可能
 この中の保存したJSON形式を、AIに読み込ませることで、AIによるアナログ時計のデザインが可能になります。

 多くの画像生成AIでは時計のデザイン画像がAIで生み出されてはいますが、実際にアナログ時計のプログラムとして動いているものは存在しません。

 今回のアプローチは、その第一歩になると思います。


③実際にやってみた

 以下のJSONはアナログ時計ジェネレータProの基本的な設定ファイルです。
 これをClaudeに読み込ませ、プロンプトを書き込んで実行します。

{
  "faceColor": "#ffffff",
  "borderColor": "#000000",
  "borderWidth": 2,
  "numberColor": "#000000",
  "hourHandColor": "#000000",
  "minuteHandColor": "#000000",
  "secondHandColor": "#ff0000",
  "dateColor": "#000000",
  "dayColor": "#000000",
  "dateFontSize": 7,
  "dayFontSize": 5,
  "dateBackgroundColor": "#ffffff",
  "dayBackgroundColor": "#ffffff",
  "size": "200",
  "fontSize": 7,
  "showDate": true,
  "showDay": true,
  "showTicks": true,
  "tickColor": "#000000",
  "majorTickLength": 4,
  "minorTickLength": 2,
  "majorTickWidth": 0.5,
  "smoothSecond": true,
  "logo": "LOGO",
  "logoSize": 10,
  "logoPosition": 35,
  "font": "Arial",
  "logoFont": "Arial",
  "logoColor": "#000000",
  "useRomanNumerals": false,
  "centerDotColor": "#000000",
  "centerDotSize": 3,
  "hourHandShape": "line",
  "minuteHandShape": "line",
  "secondHandShape": "line",
  "hourHandLength": 25,
  "hourHandWidth": 3,
  "minuteHandLength": 30,
  "minuteHandWidth": 2,
  "secondHandLength": 35,
  "secondHandWidth": 1,
  "showNumbers": true,
  "showLogo": true,
  "numberPosition": 36,
  "dateBackgroundWidth": 30,
  "dateBackgroundHeight": 14,
  "dayBackgroundWidth": 30,
  "dayBackgroundHeight": 14,
  "datePositionX": 50,
  "datePositionY": 65,
  "dayPositionX": 50,
  "dayPositionY": 75,
  "tickPosition": 45,
  "showQuarterNumbers": false,
  "faceMaterial": "plain",
  "borderMaterial": "plain",
  "hourHandTailLength": 10,
  "minuteHandTailLength": 15,
  "secondHandTailLength": 20,
  "showDateBackground": false,
  "showDayBackground": false
}

 「これは、HTMLで作られたアナログ時計の設定項目です。このJSONを自由に改良して、おもいつく時計をデザインしてみてください。」

JSONファイルでデザイン

 はい、最初にできたのがこれです。和風テイストを意識したと言っていました。

最初にAIにデザインさせたもの

④人間との共同作業で時計を作る

 初めてのものは、かなり出来がひどかったので、わたしが少し修正して、ふたたびJSONにしてAIに渡しました。

人が修正した後

 AIからは、「全体として、これらの変更により、時計のデザインはよりコンパクトで洗練されたものになっています。和風のテイストを保ちながら、読みやすさと機能性が向上しています。特に、BIZ UDPGothicフォントの使用と日付・曜日表示の調整は、日本的な要素をより強調しつつ、モダンで実用的なデザインを実現しています。」
 と言われました。
 そして、「このJSONをはじめから考慮してデザインすることが望まれますが、可能ですか?」プロンプトを入力しました。
 そして、AIが作り出したのが、この時計です。
 名付けて「和モダン」だそうです。
 ※Claudeはなぜかエレガントやモダンといった言葉が好きみたいですね。(笑)

和モダンAI作成

 そして、最後に人が手を加えて出来上がったものが、以下の時計になります。
 実際には、AIからの要望では、針を刀にしろ、文字盤の材質は紙にしろと細かく指定されましたが、設定にはありませんので、AIとの話し合いで納得してもらいました。秒針の青灰色にはかなりこだわっているようでした。

和モダンアナログ時計(AIとの共同デザイン)

 この時計の画像を添付して見せたところAIいわく、「この時計は、伝統的な日本の美意識と現代的なデザイン要素を巧みに融合させた素晴らしい作品だと思います。オフィスや家庭など、さまざまな環境に調和しつつ、独特の個性を主張できる素敵な時計になっていますね。」だそうです。
 気に入ってくれてよかった(笑)
 
なんだか、漆器っぽい感じになりました。

 コードも載せておきますね。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://fonts.googleapis.com/css2?family=Abril+Fatface&family=Cinzel&family=Poppins&family=Cormorant+Garamond&family=Quicksand&family=Comfortaa&family=Josefin+Sans&family=Playfair+Display&family=Oswald&family=Fjalla+One&family=BIZ+UDGothic&family=Old+Standard+TT&family=Noto+Serif&family=Crimson+Text&family=Kameron&family=Copse&family=Titillium+Web&family=Graduate&family=Changa+One&family=Special+Elite&family=Stardos+Stencil&family=Iceland&family=Ultra&family=Sankofa+Display&family=Edu+VIC+WA+NT+Beginner:wght@400;500;600;700&family=Kalnia+Glaze:wght@300;400;500;600;700&family=Shadows+Into+Light&family=Fredericka+the+Great&family=Orbitron:wght@400;500;600;700;800;900&family=Parisienne&display=swap" rel="stylesheet">
<title>アナログ時計 Ver2.3.3</title>
</head>
<body>
<center>
<svg id="clock" width="200" height="200" viewBox="0 0 100 100">
<defs>
<linearGradient id="faceGradient" x1="0%" y1="0%" x2="100%" y2="100%">
                
                <stop offset="0%" stop-color="#ffffff"/>
                <stop offset="40%" stop-color="#f7f3e3"/>
                <stop offset="60%" stop-color="#f0e5b7"/>
                <stop offset="100%" stop-color="#f7f3e3"/>
            
            </linearGradient>
<linearGradient id="borderGradient" x1="90%" y1="0%" x2="0%" y2="100%">
                
                <stop offset="0%" stop-color="#6c8585"/>
                <stop offset="20%" stop-color="#2c2c2c"/>
                <stop offset="50%" stop-color="#000000"/>
                <stop offset="80%" stop-color="#2c2c2c"/>
                <stop offset="100%" stop-color="#6c8585"/>
            
            </linearGradient>
<style>
  .clock-face { 
    fill: url(#faceGradient); 
    stroke: url(#borderGradient); 
    stroke-width: 7.4; 
  }
  .tick { stroke: #7d8e95 ; }
  .number { fill: #2c2c2c ; font-size: 8px; font-family: BIZ UDPGothic; text-anchor: middle; dominant-baseline: central; }
  .date-bg { fill: #e5e1d1 ; }
  .date { fill: #2c2c2c ; font-size: 6px; font-family: BIZ UDPGothic; text-anchor: middle; dominant-baseline: central; }
  .day-bg { fill: #e5e1d1 ; }
  .day { fill: #2c2c2c ; font-size: 6px; font-family: BIZ UDPGothic; text-anchor: middle; dominant-baseline: central; }
  .logo { fill: #b44c43 ; font-size: 9.8px; font-family: BIZ UDPGothic; text-anchor: middle; dominant-baseline: central; }
  .center-dot { fill: #b44c43 ; }
</style>
</defs>
<circle class="clock-face" cx="50" cy="50" r="45"/>
<g id="ticks">
<line class="tick" x1="96" y1="50" x2="89" y2="50" stroke-width="1.2"/><line class="tick" x1="95.74800718694057" y1="54.80830931031206" x2="92.76444150083574" y2="54.4947239205091" stroke-width="0.6"/><line class="tick" x1="94.99478963375506" y1="59.56393777761693" x2="92.06034683155364" y2="58.94020270516365" stroke-width="0.6"/><line class="tick" x1="93.74859974957707" y1="64.21478174124758" x2="90.8954302006916" y2="63.287730758122734" stroke-width="0.6"/><line class="tick" x1="92.02309105155965" y1="68.70988558148682" x2="89.28245467863184" y2="67.48967565225941" stroke-width="0.6"/><line class="tick" x1="89.83716857408419" y1="73" x2="83.77499074759311" y2="69.5" stroke-width="1.2"/><line class="tick" x1="87.21478174124758" y1="77.03812160545377" x2="84.78773075812273" y2="75.27476584857635" stroke-width="0.6"/><line class="tick" x1="84.18466197196014" y1="80.78000789250748" x2="81.95522749552795" y2="78.7726160734309" stroke-width="0.6"/><line class="tick" x1="80.78000789250748" y1="84.18466197196014" x2="78.7726160734309" y2="81.95522749552795" stroke-width="0.6"/><line class="tick" x1="77.03812160545377" y1="87.21478174124758" x2="75.27476584857635" y2="84.78773075812273" stroke-width="0.6"/><line class="tick" x1="73" y1="89.83716857408417" x2="69.5" y2="83.77499074759311" stroke-width="1.2"/><line class="tick" x1="68.70988558148682" y1="92.02309105155965" x2="67.48967565225941" y2="89.28245467863184" stroke-width="0.6"/><line class="tick" x1="64.21478174124758" y1="93.74859974957707" x2="63.28773075812274" y2="90.8954302006916" stroke-width="0.6"/><line class="tick" x1="59.563937777616935" y1="94.99478963375506" x2="58.94020270516366" y2="92.06034683155363" stroke-width="0.6"/><line class="tick" x1="54.80830931031206" y1="95.74800718694057" x2="54.4947239205091" y2="92.76444150083574" stroke-width="0.6"/><line class="tick" x1="50" y1="96" x2="50" y2="89" stroke-width="1.2"/><line class="tick" x1="45.191690689687945" y1="95.74800718694058" x2="45.505276079490905" y2="92.76444150083576" stroke-width="0.6"/><line class="tick" x1="40.43606222238308" y1="94.99478963375506" x2="41.05979729483636" y2="92.06034683155364" stroke-width="0.6"/><line class="tick" x1="35.78521825875242" y1="93.74859974957707" x2="36.712269241877266" y2="90.8954302006916" stroke-width="0.6"/><line class="tick" x1="31.2901144185132" y1="92.02309105155965" x2="32.5103243477406" y2="89.28245467863184" stroke-width="0.6"/><line class="tick" x1="27.00000000000001" y1="89.83716857408419" x2="30.500000000000007" y2="83.77499074759311" stroke-width="1.2"/><line class="tick" x1="22.96187839454624" y1="87.21478174124758" x2="24.72523415142366" y2="84.78773075812273" stroke-width="0.6"/><line class="tick" x1="19.219992107492523" y1="84.18466197196014" x2="21.227383926569097" y2="81.95522749552795" stroke-width="0.6"/><line class="tick" x1="15.815338028039875" y1="80.78000789250748" x2="18.044772504472057" y2="78.77261607343091" stroke-width="0.6"/><line class="tick" x1="12.785218258752423" y1="77.03812160545377" x2="15.212269241877266" y2="75.27476584857635" stroke-width="0.6"/><line class="tick" x1="10.162831425915819" y1="73" x2="16.225009252406892" y2="69.5" stroke-width="1.2"/><line class="tick" x1="7.976908948440368" y1="68.70988558148682" x2="10.71754532136817" y2="67.48967565225942" stroke-width="0.6"/><line class="tick" x1="6.25140025042294" y1="64.21478174124758" x2="9.1045697993084" y2="63.28773075812274" stroke-width="0.6"/><line class="tick" x1="5.005210366244938" y1="59.56393777761693" x2="7.939653168446355" y2="58.94020270516365" stroke-width="0.6"/><line class="tick" x1="4.251992813059431" y1="54.80830931031207" x2="7.235558499164249" y2="54.49472392050911" stroke-width="0.6"/><line class="tick" x1="4" y1="50.00000000000001" x2="11" y2="50.00000000000001" stroke-width="1.2"/><line class="tick" x1="4.251992813059424" y1="45.19169068968796" x2="7.235558499164242" y2="45.50527607949092" stroke-width="0.6"/><line class="tick" x1="5.005210366244938" y1="40.436062222383086" x2="7.939653168446355" y2="41.05979729483636" stroke-width="0.6"/><line class="tick" x1="6.25140025042294" y1="35.78521825875241" x2="9.1045697993084" y2="36.71226924187725" stroke-width="0.6"/><line class="tick" x1="7.976908948440347" y1="31.29011441851321" x2="10.717545321368156" y2="32.51032434774061" stroke-width="0.6"/><line class="tick" x1="10.162831425915826" y1="26.999999999999996" x2="16.225009252406892" y2="30.499999999999996" stroke-width="1.2"/><line class="tick" x1="12.785218258752415" y1="22.96187839454624" x2="15.212269241877252" y2="24.72523415142366" stroke-width="0.6"/><line class="tick" x1="15.815338028039868" y1="19.219992107492523" x2="18.044772504472046" y2="21.227383926569097" stroke-width="0.6"/><line class="tick" x1="19.219992107492512" y1="15.815338028039875" x2="21.227383926569086" y2="18.044772504472057" stroke-width="0.6"/><line class="tick" x1="22.96187839454623" y1="12.785218258752423" x2="24.72523415142365" y2="15.212269241877266" stroke-width="0.6"/><line class="tick" x1="26.99999999999998" y1="10.162831425915833" x2="30.499999999999982" y2="16.225009252406906" stroke-width="1.2"/><line class="tick" x1="31.290114418513195" y1="7.976908948440354" x2="32.51032434774059" y2="10.717545321368156" stroke-width="0.6"/><line class="tick" x1="35.78521825875241" y1="6.25140025042294" x2="36.71226924187725" y2="9.1045697993084" stroke-width="0.6"/><line class="tick" x1="40.43606222238305" y1="5.005210366244945" x2="41.05979729483633" y2="7.9396531684463625" stroke-width="0.6"/><line class="tick" x1="45.191690689687945" y1="4.251992813059424" x2="45.505276079490905" y2="7.235558499164242" stroke-width="0.6"/><line class="tick" x1="49.99999999999999" y1="4" x2="49.99999999999999" y2="11" stroke-width="1.2"/><line class="tick" x1="54.80830931031204" y1="4.251992813059424" x2="54.49472392050908" y2="7.235558499164242" stroke-width="0.6"/><line class="tick" x1="59.56393777761689" y1="5.005210366244931" x2="58.940202705163614" y2="7.939653168446348" stroke-width="0.6"/><line class="tick" x1="64.21478174124758" y1="6.2514002504229325" x2="63.287730758122734" y2="9.104569799308393" stroke-width="0.6"/><line class="tick" x1="68.70988558148683" y1="7.976908948440368" x2="67.48967565225942" y2="10.71754532136817" stroke-width="0.6"/><line class="tick" x1="73" y1="10.162831425915826" x2="69.5" y2="16.225009252406892" stroke-width="1.2"/><line class="tick" x1="77.03812160545375" y1="12.785218258752415" x2="75.27476584857634" y2="15.212269241877252" stroke-width="0.6"/><line class="tick" x1="80.78000789250746" y1="15.815338028039847" x2="78.77261607343088" y2="18.044772504472032" stroke-width="0.6"/><line class="tick" x1="84.18466197196014" y1="19.219992107492526" x2="81.95522749552795" y2="21.2273839265691" stroke-width="0.6"/><line class="tick" x1="87.21478174124758" y1="22.961878394546225" x2="84.78773075812273" y2="24.725234151423646" stroke-width="0.6"/><line class="tick" x1="89.83716857408416" y1="26.99999999999998" x2="83.7749907475931" y2="30.499999999999982" stroke-width="1.2"/><line class="tick" x1="92.02309105155965" y1="31.29011441851319" x2="89.28245467863184" y2="32.51032434774059" stroke-width="0.6"/><line class="tick" x1="93.74859974957707" y1="35.78521825875241" x2="90.8954302006916" y2="36.71226924187725" stroke-width="0.6"/><line class="tick" x1="94.99478963375506" y1="40.43606222238304" x2="92.06034683155363" y2="41.05979729483633" stroke-width="0.6"/><line class="tick" x1="95.74800718694057" y1="45.191690689687945" x2="92.76444150083574" y2="45.505276079490905" stroke-width="0.6"/>
</g>
<g id="numbers">
<text class="number" x="50" y="18" transform="rotate(0 50 18)">12</text><text class="number" x="66" y="22.287187078897965" transform="rotate(0 66 22.287187078897965)">1</text><text class="number" x="77.71281292110204" y="34" transform="rotate(0 77.71281292110204 34)">2</text><text class="number" x="82" y="50" transform="rotate(0 82 50)">3</text><text class="number" x="77.71281292110204" y="66" transform="rotate(0 77.71281292110204 66)">4</text><text class="number" x="66" y="77.71281292110203" transform="rotate(0 66 77.71281292110203)">5</text><text class="number" x="50" y="82" transform="rotate(0 50 82)">6</text><text class="number" x="34.00000000000001" y="77.71281292110204" transform="rotate(0 34.00000000000001 77.71281292110204)">7</text><text class="number" x="22.28718707889796" y="66" transform="rotate(0 22.28718707889796 66)">8</text><text class="number" x="18" y="50.00000000000001" transform="rotate(0 18 50.00000000000001)">9</text><text class="number" x="22.287187078897965" y="34" transform="rotate(0 22.287187078897965 34)">10</text><text class="number" x="33.999999999999986" y="22.287187078897972" transform="rotate(0 33.999999999999986 22.287187078897972)">11</text>
</g>
<g id="date">

<text id="date-text" class="date" x="44" y="64"></text>
</g>
<g id="day">

<text id="day-text" class="day" x="60" y="64"></text>
</g>
<text class="logo" x="50" y="32">和</text>
<g id="hour"></g>
<g id="minute"></g>
<g id="second"></g>
<circle class="center-dot" cx="50" cy="50" r="4"/>
</svg>
<script>
const s = {"faceColor":"#f7f3e3","borderColor":"#2c2c2c","borderWidth":"7.4","numberColor":"#2c2c2c","hourHandColor":"#b44c43","minuteHandColor":"#2c2c2c","secondHandColor":"#7d8e95","dateColor":"#2c2c2c","dayColor":"#2c2c2c","dateFontSize":"6","dayFontSize":6,"dateBackgroundColor":"#e5e1d1","dayBackgroundColor":"#e5e1d1","size":"200","fontSize":"8","showDate":true,"showDay":true,"showTicks":true,"tickColor":"#7d8e95","majorTickLength":7,"minorTickLength":3,"majorTickWidth":1.2,"smoothSecond":true,"logo":"和","logoSize":"9.8","logoPosition":"32","font":"BIZ UDPGothic","logoFont":"BIZ UDPGothic","logoColor":"#b44c43","useRomanNumerals":false,"centerDotColor":"#b44c43","centerDotSize":4,"hourHandShape":"trapezium","minuteHandShape":"trapezium","secondHandShape":"line","hourHandLength":28,"hourHandWidth":3.5,"minuteHandLength":35,"minuteHandWidth":2.5,"secondHandLength":42,"secondHandWidth":"0.8","showNumbers":true,"showLogo":true,"numberPosition":"32","dateBackgroundWidth":40,"dateBackgroundHeight":16,"dayBackgroundWidth":40,"dayBackgroundHeight":16,"datePositionX":"44","datePositionY":"64","dayPositionX":"60","dayPositionY":"64","tickPosition":46,"showQuarterNumbers":false,"faceMaterial":"satin","borderMaterial":"metallic","hourHandTailLength":12,"minuteHandTailLength":18,"secondHandTailLength":22,"showDateBackground":false,"showDayBackground":false,"importFile":"C:\\fakepath\\wamodan.json"};
const romanNumerals = ['XII', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI'];
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

function hexToHSL(hex) {
    let r = parseInt(hex.slice(1, 3), 16) / 255;
    let g = parseInt(hex.slice(3, 5), 16) / 255;
    let b = parseInt(hex.slice(5, 7), 16) / 255;

    let max = Math.max(r, g, b);
    let min = Math.min(r, g, b);
    let h, s, l = (max + min) / 2;

    if (max === min) {
        h = s = 0; // achromatic
    } else {
        let d = max - min;
        s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
        switch (max) {
            case r: h = (g - b) / d + (g < b ? 6 : 0); break;
            case g: h = (b - r) / d + 2; break;
            case b: h = (r - g) / d + 4; break;
        }
        h /= 6;
    }

    return [h * 360, s * 100, l * 100];
}

function hslToHex(h, s, l) {
    l /= 100;
    const a = s * Math.min(l, 1 - l) / 100;
    const f = n => {
        const k = (n + h / 30) % 12;
        const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
        return Math.round(255 * color).toString(16).padStart(2, '0');
    };
    return `#${f(0)}${f(8)}${f(4)}`;
}

function getCustomGradient(color, elementId, type, isBorder = false) {
    const [h, s, l] = hexToHSL(color);
    const angle = isBorder ? '90' : '0';
    let gradient;

    switch (type) {
        case 'glossy':
            const lighterColor = hslToHex(h, Math.min(100, s + 10), Math.min(100, l + 20));
            const darkerColor = hslToHex(h, Math.min(100, s + 10), Math.max(0, l - 20));
            gradient = `<stop offset="0%" stop-color="${lighterColor}"/>
                <stop offset="50%" stop-color="${color}"/>
                <stop offset="100%" stop-color="${darkerColor}"/>
            `;
            break;
        case 'metallic':
            const metallicLight = hslToHex(h, Math.min(100, s - 10), Math.min(100, l + 30));
            const metallicDark = hslToHex(h, Math.min(100, s + 20), Math.max(0, l - 30));
            gradient = `
                <stop offset="0%" stop-color="${metallicLight}"/>
                <stop offset="20%" stop-color="${color}"/>
                <stop offset="50%" stop-color="${metallicDark}"/>
                <stop offset="80%" stop-color="${color}"/>
                <stop offset="100%" stop-color="${metallicLight}"/>
            `;
            break;
        case 'pearl':
            const pearlLight = hslToHex(h, Math.max(0, s - 20), Math.min(100, l + 15));
            gradient = `
                <stop offset="0%" stop-color="${pearlLight}"/>
                <stop offset="25%" stop-color="${color}"/>
                <stop offset="50%" stop-color="${pearlLight}"/>
                <stop offset="75%" stop-color="${color}"/>
                <stop offset="100%" stop-color="${pearlLight}"/>
            `;
            break;
        case 'satin':
            const satinLight = hslToHex(h, Math.min(100, s + 5), Math.min(100, l + 10));
            const satinDark = hslToHex(h, Math.min(100, s + 10), Math.max(0, l - 10));
            gradient = `
                <stop offset="0%" stop-color="${satinLight}"/>
                <stop offset="40%" stop-color="${color}"/>
                <stop offset="60%" stop-color="${satinDark}"/>
                <stop offset="100%" stop-color="${color}"/>
            `;
            break;
        default:
            return '';
    }

    return `<linearGradient id="${elementId}" x1="${angle}%" y1="0%" x2="${isBorder ? '0' : '100'}%" y2="100%">
                ${gradient}
            </linearGradient>`;
}

function drawHand(type, angle, length, width, color, shape, tailLength = 0) {
    const rad = angle * Math.PI / 180;
    const x2 = 50 + length * Math.sin(rad);
    const y2 = 50 - length * Math.cos(rad);
    const tailX = 50 - tailLength * Math.sin(rad);
    const tailY = 50 + tailLength * Math.cos(rad);

    switch (shape) {
        case 'line':
            return `<line x1="${tailX}" y1="${tailY}" x2="${x2}" y2="${y2}" stroke="${color}" stroke-width="${width}" stroke-linecap="round"/>`;
        case 'triangle':
            const baseWidth = width * 2;
            const x3 = 50 + (baseWidth / 2) * Math.cos(rad);
            const y3 = 50 + (baseWidth / 2) * Math.sin(rad);
            const x4 = 50 - (baseWidth / 2) * Math.cos(rad);
            const y4 = 50 - (baseWidth / 2) * Math.sin(rad);
            return `<path d="M${tailX},${tailY} L${x3},${y3} L${x2},${y2} L${x4},${y4} Z" fill="${color}"/>`;
        case 'arrow':
            const totalLength = 100;
            const bodyLength = 60 / totalLength * length;
            const headLength = 40 / totalLength * length;
            const widthScale = (width / 1) * 0.1;
            const narrowWidth = (21 / totalLength * length * widthScale);
            const arrowHeadWidth = narrowWidth * 3;
            const endX = 50 + length * Math.sin(rad);
            const endY = 50 - length * Math.cos(rad);
            const bodyEndX = 50 + bodyLength * Math.sin(rad);
            const bodyEndY = 50 - bodyLength * Math.cos(rad);
            const p1 = `${tailX},${tailY}`;
            const p2 = `${tailX + narrowWidth * Math.cos(rad)},${tailY + narrowWidth * Math.sin(rad)}`;
            const p3 = `${bodyEndX + narrowWidth * Math.cos(rad)},${bodyEndY + narrowWidth * Math.sin(rad)}`;
            const p4 = `${bodyEndX + arrowHeadWidth * Math.cos(rad)},${bodyEndY + arrowHeadWidth * Math.sin(rad)}`;
            const p5 = `${endX},${endY}`;
            const p6 = `${bodyEndX - arrowHeadWidth * Math.cos(rad)},${bodyEndY - arrowHeadWidth * Math.sin(rad)}`;
            const p7 = `${bodyEndX - narrowWidth * Math.cos(rad)},${bodyEndY - narrowWidth * Math.sin(rad)}`;
            const p8 = `${tailX - narrowWidth * Math.cos(rad)},${tailY - narrowWidth * Math.sin(rad)}`;
            return `<path d="M ${p1} L ${p2} L ${p3} L ${p4} L ${p5} L ${p6} L ${p7} L ${p8} Z" fill="${color}"/>`;
        case 'diamond':
            const diamondWidth = width * 2;
            const midLength = length * 0.333;
            const dmx1 = 50 + midLength * Math.sin(rad) + (diamondWidth / 2) * Math.cos(rad);
            const dmy1 = 50 - midLength * Math.cos(rad) + (diamondWidth / 2) * Math.sin(rad);
            const dmx2 = 50 + midLength * Math.sin(rad) - (diamondWidth / 2) * Math.cos(rad);
            const dmy2 = 50 - midLength * Math.cos(rad) - (diamondWidth / 2) * Math.sin(rad);
            return `<path d="M${tailX},${tailY} L${dmx1},${dmy1} L${x2},${y2} L${dmx2},${dmy2} Z" fill="${color}"/>`;
        case 'leaf':
            const leafWidth = width * 3;
            const lx1 = 50 + (leafWidth / 2) * Math.cos(rad);
            const ly1 = 50 + (leafWidth / 2) * Math.sin(rad);
            const lx2 = 50 - (leafWidth / 2) * Math.cos(rad);
            const ly2 = 50 - (leafWidth / 2) * Math.sin(rad);
            const lxMid = 50 + (length * 0.7) * Math.sin(rad);
            const lyMid = 50 - (length * 0.7) * Math.cos(rad);
            return `<path d="M${tailX},${tailY} Q${lx1},${ly1} ${x2},${y2} Q${lx2},${ly2} ${tailX},${tailY} L${lxMid},${lyMid}" fill="${color}"/>`;
        case 'pentagon':
            const pentagonWidth = width * 2;
            const triangleStart = length * 0.7;
            const p1x = tailX + (pentagonWidth / 2) * Math.cos(rad);
            const p1y = tailY + (pentagonWidth / 2) * Math.sin(rad);
            const p2x = tailX - (pentagonWidth / 2) * Math.cos(rad);
            const p2y = tailY - (pentagonWidth / 2) * Math.sin(rad);
            const p3x = 50 + triangleStart * Math.sin(rad) + (pentagonWidth / 2) * Math.cos(rad);
            const p3y = 50 - triangleStart * Math.cos(rad) + (pentagonWidth / 2) * Math.sin(rad);
            const p4x = 50 + triangleStart * Math.sin(rad) - (pentagonWidth / 2) * Math.cos(rad);
            const p4y = 50 - triangleStart * Math.cos(rad) - (pentagonWidth / 2) * Math.sin(rad);
            return `<path d="M${p1x},${p1y} L${p3x},${p3y} L${x2},${y2} L${p4x},${p4y} L${p2x},${p2y} Z" fill="${color}"/>`;
        case 'pencil':
            const pencilWidth = width * 1.5;
            return `
                <path d="M${tailX},${tailY} L${x2},${y2}" stroke="${color}" stroke-width="${width}" fill="none"/>
                <circle cx="${x2}" cy="${y2}" r="${pencilWidth / 2}" fill="${color}"/>
            `;
        case 'breguet':
            const breguetHoleRadius = width * 1.5;
            const breguetHoleDistance = length * 0.8;
            const bhx = 50 + breguetHoleDistance * Math.sin(rad);
            const bhy = 50 - breguetHoleDistance * Math.cos(rad);
            return `
                <line x1="${tailX}" y1="${tailY}" x2="${x2}" y2="${y2}" stroke="${color}" stroke-width="${width}" stroke-linecap="round"/>
                <circle cx="${bhx}" cy="${bhy}" r="${breguetHoleRadius}" fill="white" stroke="${color}" stroke-width="${width * 0.3}"/>
            `;
        case 'spade':
            const spadeWidth = width * 3.5;
            const tearDropStart = length * 0.7;
            const tearDropLength = length - tearDropStart;
            const lx = 50 + tearDropStart * Math.sin(rad);
            const ly = 50 - tearDropStart * Math.cos(rad);
            const cx1 = lx + (tearDropLength * 0.5) * Math.sin(rad) + (spadeWidth * 0.5) * Math.cos(rad);
            const cy1 = ly - (tearDropLength * 0.5) * Math.cos(rad) + (spadeWidth * 0.5) * Math.sin(rad);
            const cx2 = lx + (tearDropLength * 0.5) * Math.sin(rad) - (spadeWidth * 0.5) * Math.cos(rad);
            const cy2 = ly - (tearDropLength * 0.5) * Math.cos(rad) - (spadeWidth * 0.5) * Math.sin(rad);
            return `
                <path d="M${tailX},${tailY} L${lx},${ly}" stroke="${color}" stroke-width="${width}" stroke-linecap="round"/>
                <path d="M${lx},${ly} Q${cx1},${cy1} ${x2},${y2} Q${cx2},${cy2} ${lx},${ly} Z" fill="${color}"/>
            `;
        case 'rectangle':
            const halfWidth = width / 2;
            const tipX1 = x2 + halfWidth * Math.cos(rad);
            const tipY1 = y2 + halfWidth * Math.sin(rad);
            const tipX2 = x2 - halfWidth * Math.cos(rad);
            const tipY2 = y2 - halfWidth * Math.sin(rad);
            const baseX1 = tailX + halfWidth * Math.cos(rad);
            const baseY1 = tailY + halfWidth * Math.sin(rad);
            const baseX2 = tailX - halfWidth * Math.cos(rad);
            const baseY2 = tailY - halfWidth * Math.sin(rad);
            return `
                <polygon points="${tipX1},${tipY1} ${tipX2},${tipY2} ${baseX2},${baseY2} ${baseX1},${baseY1}" fill="${color}"/>
            `;
        case 'trapezium':
            const trapWidth = width * 1.5;
            const trapNarrowWidth = width * 0.9;
            const trapMidPoint = length * 0.7;
            const tx1 = tailX + (width / 2) * Math.cos(rad);
            const ty1 = tailY + (width / 2) * Math.sin(rad);
            const tx2 = tailX - (width / 2) * Math.cos(rad);
            const ty2 = tailY - (width / 2) * Math.sin(rad);
            const tx3 = 50 + (trapWidth / 2) * Math.cos(rad);
            const ty3 = 50 + (trapWidth / 2) * Math.sin(rad);
            const tx4 = 50 - (trapWidth / 2) * Math.cos(rad);
            const ty4 = 50 - (trapWidth / 2) * Math.sin(rad);
            const tx5 = x2 + (trapNarrowWidth / 2) * Math.cos(rad);
            const ty5 = y2 + (trapNarrowWidth / 2) * Math.sin(rad);
            const tx6 = x2 - (trapNarrowWidth / 2) * Math.cos(rad);
            const ty6 = y2 - (trapNarrowWidth / 2) * Math.sin(rad);
            return `
                <path d="M${tx1},${ty1} L${tx3},${ty3} L${tx5},${ty5} L${tx6},${ty6} L${tx4},${ty4} L${tx2},${ty2} Z" fill="${color}"/>
            `;
        default:
            return '';
    }
}

function u(){
const n=new Date(),h=n.getHours()%12,m=n.getMinutes(),sc=n.getSeconds(),ms=n.getMilliseconds(),
ha=(h+m/60)*30,ma=(m+sc/60)*6,sa=true?(sc+ms/1000)*6:sc*6;
document.getElementById('hour').innerHTML = drawHand('hour', ha, 28, 3.5, '#b44c43', 'trapezium', 12);
document.getElementById('minute').innerHTML = drawHand('minute', ma, 35, 2.5, '#2c2c2c', 'trapezium', 18);
document.getElementById('second').innerHTML = drawHand('second', sa, 42, 0.8, '#7d8e95', 'line', 22);
document.getElementById('date-text').textContent=`${n.getMonth()+1}/${n.getDate()}`;
document.getElementById('day-text').textContent=days[n.getDay()];
requestAnimationFrame(u);
}
u();
</script>
</center>
</body>
</html>

⑤おわりに

 いかがでしたでしょうか?
 はっきりいって、私には決して思いつかない、デザインできない時計です。
 稚拙なところもありますが、なんとなく日本好きの西洋人っぽさを感じるのは私だけでしょうか?

日本好きの西洋人


この記事が参加している募集

この記事が気に入ったらサポートをしてみませんか?