このトピックでは、iPhone用のシンプルなグラフィカルエディターの作成方法を示します。 記事はできるだけ明確に書かれているため、初心者でも簡単に理解できます。 さらに、私は教えます:
- タッチデバイスイベントの機能について;
- モバイルデバイスのレイアウト機能について。
- 通常の「応接室」を作成する理由には、いくつかのキャンバスを使用する必要があります。
- クリックジャッキングとは何ですか、なぜこのハックを私の図面で使用したのですか?
- 開発プロセスで遭遇したすべての困難といくつかのささいなことについて。
詳細-カットの下
エントリー
誰もがiPhone開発とHTML5 Canvasテクノロジーの両方に興味を持っていると思いますが、さまざまな理由で彼はそれを放棄しました。 この記事では、このようなアプリケーションの作成が非常に簡単であることを示したいと思います。 コードを透明にするために、サードパーティのライブラリとフレームワークを使用しませんでした。
それでは始めましょう。
CSSスタイル
最初はhtmlコードから始めようと思っていましたが、何が起きているのかを完全に理解するには、まずスタイルを提供する必要があることに気付きました。
body {
margin: 0;
padding: 0;
background: #fff;
}
/* , */
.tb {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 40px;
overflow: hidden;
border-bottom: 1px solid #CCC;
background-color: orange;
}
canvas {
position: absolute;
top: 40px;
left: 0;
}
/* */
.bt {
overflow: hidden;
float: left;
font-weight: bold;
color: white;
font: 16px Arial;
width: 33%;
padding-top: 10px;
height: 30px;
text-align: center;
}
/* select */
select {
float: left;
width: 33%;
margin-top: 10px;
height: 20px;
}
HTMLファイル
適切なトピックサイズを維持するために、ファイル全体を公開するのではなく、興味深い部分のみを公開することにしました。
メタビューポート
... <head> <meta name="viewport" content="width=device-width,user-scalable=no" /> </head> ...
ビューポート属性を持つメタタグは、ページの幅(幅)、高さ(高さ)、ユーザーがこのページのズームを変更できるようにするかどうか(ユーザースケーラブルな値)、ページを読み込む最初のズーム(初期スケール)、最小(最小スケール)および最大(最大スケール)ズームになり得るもの。 現在のデバイスの幅と高さは、値device-width / device-heightに保存されます。
ブラシの色/サイズを選択します
私は長い間色を選択する方法を考えていましたが、私に起こった最も簡単なことは、 選択タグを希望の色とサイズの値で選択要素を作成し、 「色」または「サイズ」ボタンをクリックして選択要素にフォーカスを送ることでした。 iPhoneに焦点を当てたselectタグがこのような動作をすることは知られています 。
フォーカス(メソッド。 フォーカス() )は、残念ながら送信したくありませんでした。 しかし、私はあきらめませんでした! そして、私が考えたのは、必要な選択要素を含む透明なdivを作成した場合です。 そして、このdivは、次に私のボタンを付けますか?! そして、あなたはどう思いますか? すべてが機能します! 明確にするために、次の方法を示します。
ハックコードを持ち込みます。
<!-- , --> <div id="tb" style="z-index: 2"> <div class="bt"></div> <div class="bt"></div> <a class="bt"></a> </div> <!-- , --> <div id="htb" style="opacity: 0; z-index: 3"> <select id="hcs"> <option>blue</option> <option>red</option> <option>green</option> <!-- --> </select> <select id="hss"> <option>10</option> <option>11</option> <option>12</option> <!-- --> </select> <a class="bt" id="savebutton" style="z-index: 20;"></a> </div>
(たとえば、別のサイトからの透過フレームがある場合、これはクリックジャッキングと呼ばれます)
最も興味深い
リーダーに100行のコードを読み込む前に、すべてがどのように機能するのか、そしてその理由を説明したいと思います。
タッチデバイスイベント
ontouchstart-ユーザーが画面上で指を動かし始めたときにトリガーされます(コンピューターのonmousedownと比較できます)。
ontouchmove-ユーザーが画面をスワイプするとトリガーされます( onmousemoveと比較できます)。
ontouchend-ユーザーが画面から指を離したときにトリガーされます( onmouseupと比較できます)。
Gesture APIからのongesturechange 、 ongestureendもありますが、この記事では考慮しません。
各イベントはタッチの配列を返し、その各要素には次のようなプロパティが含まれます。
pageX 、 pageY 、 clientX 、 clientY 。 touches配列の要素の数は、画面に触れる指の数によって異なります。
したがって、画面のタッチの追跡と座標の取得は非常に簡単です。
someElement.ontouchstart = function(e) { console.log("X: " + e.touches[0].pageX + ", Y:" + e.touches[0].pageY); }
アルゴリズム
すべてはどのように機能しますか? ポイントで
- ontouchmove / ontouchstartイベントがトリガーされると、キャンバスがクリアされ、指の動きの座標で構成される配列にもう1つの値が書き込まれ、すべてが再描画されます。
- ontouchendイベントがトリガーされると、メインキャンバスに描画されたすべてが画像に保存され、この画像はメインキャンバスの下にある透明なアシスタントキャンバスにコピーされます。 その後、メインキャンバスがクリアされ、新しいontouchmove / ontouchstartを待機します
なぜすべてがそんなに複雑なのですか?
実際、ベジェ曲線はレンダリング関数で使用されています。 次の制御座標が反対側にある場合、曲線(より頻繁に-終点)を変更できます。 通常の直線を使用する場合、高速の円運動では、美しい角度ではないことが観察できます。
すべてを2番目のキャンバスに再描画する理由
先ほど言ったように、各「ストローク」の後、メインキャンバスの画像はアシスタントキャンバスに複製されます。 これは、メインキャンバスの座標配列を詰まらせないようにするためです。 配列内の制御点が多いほど、描画が遅くなります。 これらの理由により、再描画バッファーを1ストロークに制限しました(1つの大きなストロークを描画すると、速度も低下する可能性があります)。
コード
キャンバスを作成してコンテキストを取得するためのコード
var width = window.innerWidth; // var height = window.innerHeight; // var hcanv = document.createElement("canvas"); // var mcanv = document.createElement("canvas"); // // mcanv.width = width; mcanv.height = height; hcanv.width = width; hcanv.height = height; // DOM document.body.appendChild(mcanv); document.body.appendChild(hcanv); hcanv.style.zIndex = 10; // // var mctx = mcanv.getContext("2d"); var hctx = hcanv.getContext("2d");
色とブラシサイズの選択の処理
var selects = document.getElementsByTagName("select"); // select for(var i=0; i<selects.length; i++) { // selects[i].onchange = handleSelects; // } function handleSelects() { // var val = this.options[this.selectedIndex].value; switch(this.id) { // , case "hcs": brush.color = val; // break; case "hss": brush.size = val; // break; } }
描画コード
var touches = {x:[], y:[]}; // var brush = {color: "blue", size: 10}; // var snapshot = ""; // hcanv.ontouchstart = function(e) { // // touches.x.push(e.touches[0].pageX); touches.y.push(e.touches[0].pageY-40); //40 - hctx.clearRect(0, 0, width, height); // redraw(hctx); // () return false; // - } hcanv.ontouchmove = function(e) { // // touches.x.push(e.touches[0].pageX); touches.y.push(e.touches[0].pageY-40); hctx.clearRect(0, 0, width, height); // redraw(hctx); // () return false; // - ( etc) } hcanv.ontouchend = function(e) { // snapshot = hcanv.toDataURL(); // URL png var img = new Image(); // img.src = snapshot; // url img.onload = function() { // ( ) mctx.drawImage(img, 0, 0); // hctx.clearRect(0, 0, width, height); // } // touches.x = []; touches.y = []; } // function redraw(ctx) { ctx.lineCap = "round"; // ctx.lineJoin = "round"; // ctx.strokeStyle = brush.color; // ctx.lineWidth = brush.size; // ctx.beginPath(); if(touches.x.length < 2) { //, ctx.moveTo(touches.x[0], touches.y[0]); ctx.lineTo(touches.x[0] + 0.51, touches.y[0]); ctx.stroke(); ctx.closePath(); return; } ctx.moveTo(touches.x[0], touches.y[0]); ctx.lineTo((touches.x[0] + touches.x[1]) * 0.5, (touches.y[0] + touches.y[1]) * 0.5); var i = 0; while(++i < (touches.x.length -1)) { var abs1 = Math.abs(touches.x[i-1] - touches.x[i]) + Math.abs(touches.y[i-1] - touches.y[i]) + Math.abs(touches.x[i] - touches.x[i+1]) + Math.abs(touches.y[i] - touches.y[i+1]); var abs2 = Math.abs(touches.x[i-1] - touches.x[i+1]) + Math.abs(touches.y[i-1] - touches.y[i+1]); if(abs1 > 10 && abs2 > abs1 * 0.8) { //, ctx.quadraticCurveTo(touches.x[i], touches.y[i], (touches.x[i] + touches.x[i+1]) * 0.5, (touches.y[i] + touches.y[i+1]) * 0.5); continue; } ctx.lineTo(touches.x[i], touches.y[i]); ctx.lineTo((touches.x[i] + touches.x[i+1]) * 0.5, (touches.y[i] + touches.y[i+1]) * 0.5); } ctx.lineTo(touches.x[touches.x.length-1], touches.y[touches.y.length-1]); ctx.moveTo(touches.x[touches.x.length-1], touches.y[touches.y.length-1]); ctx.stroke(); ctx.closePath(); }
デモ
ソースコード
PSご質問がある場合は、お問い合わせください! 答えようとします。