.no-scrollbar { scrollbar-width: none; } .no-scrollbar::-webkit-scrollbar { display: none; }
https://developer.mozilla.org/en-US/docs/Web/CSS/::-webkit-scrollbar
https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-width
Canvas上に線を引くイベントをtouchmoveにつけると、スマートフォンでピンチイン・ピンチアウトなどをしようとした時に、線が引かれるようになってしまう。
ウェブ開発をしたことある人なら、Canvas外で操作すれば、問題ないことを知っているだろうけど、普通はそういうことには気がつかないことが大半だろうと思われる。
利用者にとってCanvasを使いやすくするためには、その点も配慮したほうが使いやすくなるはずである。
<div id="wrap"> <canvas id="canvas" /> </div> <style> #wrap { width: 100vw; height: 100vh; position: relative; } #canvas { position: absolute; width: 500px; height: 200px; top: 0; bottom: 0; right: 0; left: 0; margin: auto; } </style>
const canvas = document.getElementById('canvas');
const canvasWrapper = document.getElementById('wrap');
// @var {Bool} isDrawing 描画可能か
let isDrawing = false;
canvasWrapper.addEventListener('touchstart', (event) => {
if (
event.touches.length === 1
&& document.elementFromPoint(event.touches[0].clientX, event.touches[0].clientY) === canvas
) {
isDrawing = true;
} else {
// canvasWrapperとcanvasの境目辺りをタッチした場合、稀にスクロールイベントが先に発火してしまうことがある。
// それを抑制するために、イベントの伝播を止める
event.stopPropagation();
isDrawing = false;
}
});
canvas.addEventListener('touchmove', (event) => {
if (!isDrawing) {
return;
}
event.preventDefault();
// ここに描画処理
});
touchstart
イベントは、canvasではなく親要素に付けているところがポイント。
CakePHPにおいてモデルの各イベント間で値を共有したい場面がたまにある。
たとえば、 beforeSave
でしか取得できない値を afterSave
で使いたいとか、エンティティの値によって buildRules
のルールに値を渡したいなど。
そういう場合、以下の方法で値の受け渡しができるようになる:
// モデル内で
public function beforeSave(Event $event, EntityInterface $entity, ArrayObject $options)
{
$options['hogehoge'] = 'hugahuga';
}
public function afterSave(Event $event, EntityInterface $entity, ArrayObject $options)
{
debug($options['hogehoge']); // => 'hugahuga';
}
各イベントの $options
はイベント間で同じものが使いまわされているため、先に発生するイベントで追加された値は、後のイベントで利用できるようになる。
上記の例では、 beforeSave
で追加した $options['hogehoge']
を afterSave
で利用できるようになっている。
また、この $options
は ルールチェッカー の $rules->add
の引数としても利用されているため、そこでも利用できる。
public function beforeSave(Event $event, EntityInterface $entity, ArrayObject $options)
{
$options['foo'] = true;
}
public function buildRules(RulesChecker $rules)
{
$rules->add(function ($entity, $options) {
debug($options['foo']); // => true;
}, 'ruleName');
return $rules;
}
上記の例では、単純に固定値を設定しているだけだが、複数のルールで同じモデルの同じレコードを参照しないといけない場合など、あらかじめ beforeSave
などで、その共通で参照しないといけないデータを取得して $options
に追加しておけば、ルール毎にfindする必要がなくなり、多少は高速化が見込める。
同じコードを何度も書く必要がなくなり、見通しがよくなるはず。
id | name | |
---|---|---|
1 | hoge | example.hoge@example.com |
2 | huga | example.huga@example.com |
3 | piyo | example.piyo@example.com |
このようなテーブル( users
とする)があった時、次のコードで id
を一次元配列で一発で取得できる:
$result = TableRegistry::getTableLocator()->get('Users')->find()->select('Users.id')->extract('id')->toList(); // $result = [1, 2, 3]
email
だけを取得したい場合も同様:
$result = TableRegistry::getTableLocator()->get('Users')->find()->select('Users.email')->extract('email')->toList(); // $result = ['example.hoge@example.com', 'example.huga@example.com', 'example.piyo@example.com']
このコードは CakePHP3 以上であれば動くはず
スマホやタブレットでタッチ操作をしている場合、 DragEvent が使えないため、 TouchEvent を利用して擬似的にドラッグ&ドロップを実現する必要がある。
本記事はそのヒント。
まずはじめに、ドロップイベントを実現するには、 touchend
イベントと document.elementFromPoint を組み合わせればよい。
touchend
イベントが発火した場所の要素を document.elementFromPoint
で取得し、その取得できた要素がなにかを判定して、すきな処理を行えばよい。
例:
/**
* @param {Event} event イベントオブジェクト
* @return {Element|null}
*/
const getElementFromEventPoint = (event) => {
const { clientX, clientY } = /^touch/.test(event.type)
? event.changedTouches[0] // touchstart|touchmove|touchendの場合はこちらから座標を取得
: event; // その他(マウスやポインタ)のイベントの場合は、こちらから座標を取得
return document.elementFromPoint(clientX, clientY); // 座標から要素を取得
}
let isDragging = false;
window.addEventListener('touchstart', () => {
isDragging = true;
});
window.addEventListener('touchend', (event) => {
if (!isDragging) {
return;
}
isDragging = false;
const el = getElementFromEventPoint(event);
// イベントが発生した座標の要素に `data-drop-zone` 属性が付与されていたらなにかしら処理する
if (el && typeof el.dataset.dropZone !== 'undefined') {
// 処理...
}
});
この例では、 touchend
でドロップを再現しているけれど、同じ要領で touchmove
で dragover
を再現したりすることが可能。
https://codepen.io/riffrain/pen/yLLWYgq の様に、よくあるドラッグ&ドロップを簡単にするパッケージと組み合わせたり、それの拡張( https://codepen.io/riffrain/pen/yLLWYgq では、本来ドロップできない外の要素でドロップを検知している)をすることができる
オブジェクトをフラットにしたり、フラットなオブジェクトを元に戻したりするJS
lodash の zipObjectDeep
に依存
CakePHPのHash::flatten のようなもの
zipObjectDeep
で展開するため、キーの構成は Hash::flatten
とは異なる
import _ from 'lodash';
const numberRE = /^(?:0|[1-9][0-9]*)$/;
const flatten = (originalData) => {
const result = {};
const stack = [];
let paths = [];
let data = JSON.parse(JSON.stringify(originalData));
while(true) {
data = { ...data };
const key = Object.keys(data).shift();
if (key) {
const element = data[key];
delete data[key];
if (element !== null && typeof element === 'object' && Object.keys(element).length) {
if (data) {
stack.push([data, [...paths]]);
}
data = element;
if (numberRE.test(key)) {
if (paths.length && paths[paths.length - 1] === '.') {
paths.pop();
}
paths.push(`[${key}]`);
} else {
paths.push(key);
}
paths.push('.');
} else {
result[`${paths.join('')}${key}`] = element;
}
}
if (!Object.keys(data).length) {
if (stack.length) {
[data, paths] = stack.shift();
} else {
break;
}
}
}
return result;
}
const expand = (data) => {
const keys = [];
const values = [];
const dataKeys = Object.keys(data);
let key = dataKeys.shift();
while(key) {
keys.push(`result.${key}`);
values.push(data[key]);
key = dataKeys.shift();
}
const { result } = _.zipObjectDeep(keys, values);
return result;
}
Prismicで用意されているサンプルを動く環境を構築する
https://prismic.io/docs/technologies/sample-blog-with-prismic-and-gatsby を参考にPrismicとGatuby.jsの設定をする
※2021/01/23時点、リンク先に通りに作成したらgatsby-node.js 等でエラーが出るので自分で解消する必要がある
Netlifyに設定を追加する
基本的には、GatsbyCloudの設定と同じで、環境変数やWebhookの設定を適宜NetlifyのものにすればOK