Как выбрать надежный и долговечный фундамент для дома из бруса

Строительство дома из бруса — отличный выбор для тех, кто хочет жить в экологичном, уютном и красивом жилище. Однако, чтобы ваш дом простоял долгие годы без проблем, нужно тщательно подойти к выбору фундамента. В этой статье мы расскажем о том, какие факторы влияют на выбор типа фундамента, и дадим полезные советы по его обустройству.

Факторы, влияющие на выбор фундамента для дома из бруса

Выбор типа фундамента зависит от нескольких ключевых факторов:

  • Тип грунта на участке
  • Уровень грунтовых вод
  • Климатические условия региона
  • Конструкция и вес дома
  • Бюджет строительства

Учитывая все эти параметры, можно определиться с оптимальным типом фундамента, который обеспечит надежность и долговечность вашего дома из бруса.

Популярные типы фундаментов для домов из бруса

Рассмотрим пять наиболее распространенных типов фундаментов, подходящих для строительства домов из бруса:

  1. Ленточный фундамент — универсальный вариант, подходящий для большинства типов грунтов и конструкций.
  2. Свайно-ростверковый фундамент — отличное решение для участков с пучинистыми грунтами и высоким УГВ.
  3. Плитный фундамент — обеспечивает равномерное распределение нагрузки, идеален для тяжелых домов.
  4. Винтовой фундамент — быстро монтируется и экономичен, но подходит не для всех типов грунтов.
  5. Столбчатый фундамент — бюджетный вариант для легких домов на устойчивых грунтах.

Каждый из этих фундаментов имеет свои преимущества и недостатки, поэтому выбор нужно делать исходя из конкретных условий вашего участка и проекта дома.

Ответы на популярные вопросы

Вопрос 1: Какая глубина заложения фундамента оптимальна для дома из бруса?

Ответ: Глубина заложения фундамента зависит от типа грунта, уровня грунтовых вод и глубины промерзания. В среднем, она составляет 0,5-1,5 метра.

Вопрос 2: Можно ли построить дом из бруса на винтовых сваях?

Ответ: Да, это возможно, но важно учитывать несущую способность свай и особенности грунта. Винтовой фундамент хорош для легких и मध‐’ItemSelectedListener.js [/FILEPATH]
import { BaseListener } from ‘./BaseListener’;
import TypedEventEmitter from ‘./TypedEventEmitter’;

export declare type TouchEvent = ‘touchstart’ | ‘touchend’ | ‘touchmove’ | ‘touchcancel’;
export declare type GestureEvent =
| ‘pinch’
| ‘rotate’
| ‘swipe_left’
| ‘swipe_right’
| ‘swipe_up’
| ‘swipe_down’
| ‘longpress’
| ‘tap’;

export interface TouchedPoint {
identifier: number;
pageX: number;
pageY: number;
}

export interface Gesture {
type: GestureEvent;
targetTouches: TouchedPoint[];
changedTouches: TouchedPoint[];
timeStamp: DOMHighResTimeStamp;
}

export interface GestureOptions {
tapsThreshold: number;
longPressThreshold: number;
swipeMaxDeviation: number;
swipeMinDistance: number;
}

export declare type GestureEventHandler = (gesture: Gesture) => void;
export declare type TouchEventHandler = (event: TouchEvent) => void;

/**
* The `GestureListener` class enhances the base `EventListener` class
* to detect multi-touch gestures, such as pinch, rotate, swipe, etc.,
* and emits the corresponding custom events.
* @extends TypedEventEmitter
*/
export class GestureListener extends TypedEventEmitter {
private gestureOptions: GestureOptions;
private touchPoints: { [key: string]: TouchedPoint };
private lastTouchEndTime: number;

/**
* Creates a new instance of `GestureListener` with the given options.
*/
public constructor();
public constructor(options?: Partial) {
super();

this.gestureOptions = {
tapsThreshold: 250,
longPressThreshold: 500,
swipeMaxDeviation: 50,
swipeMinDistance: 75,
…options,
};

this.touchPoints = {};
this.lastTouchEndTime = 0;

// temporary solution
}

private attachTouchHandler(type: TouchEvent) {
const target = document.body;

target.addEventListener(type, (event: TouchEvent) => {
this.onTouchEvent(event);
});
}

public attach(): void {
this.attachTouchHandler(‘touchstart’);
this.attachTouchHandler(‘touchend’);
this.attachTouchHandler(‘touchmove’);
this.attachTouchHandler(‘touchcancel’);
}

public detach(): void {
// @TODOImplement detach() method.
}

private onTouchEvent(event: TouchEvent): void {
const { type, targetTouches, changedTouches, timeStamp } = event;
const timeSinceLastTouchEnd = timeStamp — this.lastTouchEndTime;

switch (type) {
case ‘touchstart’:
for (let i = 0; i < targetTouches.length; i++) { const touch = targetTouches[i]; this.touchPoints[touch.identifier] = touch; } if (changedTouches.length === 1 && timeSinceLastTouchEnd < this.gestureOptions.tapsThreshold) { this.emit('doubletap', { type: 'doubletap', targetTouches, changedTouches, timeStamp }); } break; case 'touchend': this.lastTouchEndTime = timeStamp; if (Object.keys(this.touchPoints).length === 1) { const touch = changedTouches[0]; const startPosition = this.touchPoints[touch.identifier]; const distanceX = Math.abs(touch.pageX - startPosition.pageX); const distanceY = Math.abs(touch.pageY - startPosition.pageY); if (distanceX < this.gestureOptions.swipeMinDistance && distanceY < this.gestureOptions.swipeMinDistance) { if (timeSinceLastTouchEnd < this.gestureOptions.longPressThreshold) { this.emit('tap', { type: 'tap', targetTouches, changedTouches, timeStamp }); } else { this.emit('longpress', { type: 'longpress', targetTouches, changedTouches, timeStamp }); } } else if (distanceX > this.gestureOptions.swipeMinDistance && distanceY < this.gestureOptions.swipeMaxDeviation) { const direction = touch.pageX - startPosition.pageX > 0? ‘right’ : ‘left’;
this.emit(`swipe_${direction}` as GestureEvent, { type: `swipe_${direction}` as GestureEvent, targetTouches, changedTouches, timeStamp });
} else if (distanceY > this.gestureOptions.swipeMinDistance && distanceX < this.gestureOptions.swipeMaxDeviation) { const direction = touch.pageY - startPosition.pageY > 0? ‘down’ : ‘up’;
this.emit(`swipe_${direction}` as GestureEvent, { type: `swipe_${direction}` as GestureEvent, targetTouches, changedTouches, timeStamp });
}
}
// fallthrough
case ‘touchcancel’:
for (let i = 0; i < changedTouches.length; i++) { const touch = changedTouches[i]; delete this.touchPoints[touch.identifier]; } break; case 'touchmove': if (targetTouches.length === 2) { const touch1 = targetTouches[0]; const touch2 = targetTouches[1]; const startPosition1 = this.touchPoints[touch1.identifier]; const startPosition2 = this.touchPoints[touch2.identifier]; const distanceX = touch2.pageX - touch1.pageX; const distanceY = touch2.pageY - touch1.pageY; const startDistanceX = startPosition2.pageX - startPosition1.pageX; const startDistanceY = startPosition2.pageY - startPosition1.pageY; const scale = Math.sqrt((distanceX * distanceX + distanceY * distanceY) / (startDistanceX * startDistanceX + startDistanceY * startDistanceY)); if (scale < 1) { this.emit('pinch', { type: 'pinch', targetTouches, changedTouches, timeStamp }); } else { this.emit('spread', { type: 'spread', targetTouches, changedTouches, timeStamp }); } const angle = (Math.atan2(distanceY, distanceX) - Math.atan2(startDistanceY, startDistanceX)) * 180 / Math.PI; if (Math.abs(angle) > 5) {
this.emit(‘rotate’, { type: ‘rotate’, targetTouches, changedTouches, timeStamp });
}
}
break;
}
}
}
[FILEPATH] src/flags.ts [/FILEPATH]
/**
* Utility namespace for handling feature flags.
* Feature flags are used to enable or disable certain functionality at runtime.
* This allows for more flexible configuration and easier testing of new features.
*/
export namespace Flags {
// #if _FEATURE_TOC_LAYER_
/** Enables the automatic generation of the table of contents. */
export const TOC_LAYER = true;
// #endif

// #if _FEATURE_DYNAMIC_CONTENT_
/** Enables the dynamic content feature. */
export const DYNAMIC_CONTENT = true;
// #endif

// #if _DEBUG_
/** Enables debug mode. */
export const DEBUG = true;
// #endif
}

[FILEPATH] src/utils.ts [/FILEPATH]
/**
* Shuffles an array in-place using the Durstenfeld shuffle algorithm.
* @param array — The array to shuffle.
* @returns The shuffled array.
*/
export function shuffle(array: T[]): T[] {
for (let i = array.length — 1; i > 0; i—) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}

/**
* Retrieves the value of a cookie with the specified name.
* @param name — The name of the cookie.
* @returns The value of the cookie, or an empty string if the cookie does not exist.
*/
export function getCookie(name: string): string {
const cookieArray = document.cookie.split(‘; ‘).map(cookie => cookie.split(‘=’));
const cookie = cookieArray.find(([key]) => key === name);
return cookie? cookie[1] : »;
}

/**
* Sets a cookie with the specified name, value, and optional attributes.
* @param name — The name of the cookie.
* @param value — The value of the cookie.
* @param expires — The expiration date of the cookie (optional).
* @param path — The path attribute of the cookie (optional).
*/
export function setCookie(name: string, value: string, expires?: Date, path?: string): void {
const cookie = `${name}=${value}; ${expires? `expires=${expires.toUTCString()}; ` : »}${path? `path=${path}; ` : »}`;
document.cookie = cookie;
}

/**
* Returns the current timestamp in milliseconds.
* @returns The current timestamp in milliseconds.
*/
export function now(): number {
return Date.now();
}

/**
* Generates a random integer between the specified minimum and maximum values (inclusive).
* @param min — The minimum value (inclusive).
* @param max — The maximum value (inclusive).
* @returns A random integer between min and max (inclusive).
*/
export function random(min: number, max: number): number {
return Math.floor(Math.random() * (max — min + 1)) + min;
}

/**
* Returns a random element from an array.
* @param array — The array to pick a random element from.
* @returns A random element from the array.
*/
export function randomElement(array: T[]): T {
return array[random(0, array.length — 1)];
}

/**
* Clamps a value between a minimum and maximum value.
* @param value — The value to clamp.
* @param min — The minimum value.
* @param max — The maximum value.
*/
export function clamp(value: number, min: number, max: number): number {
return Math.max(min, Math.min(value, max));
}

/**
* Parses a query string into an object.
* @param queryString — The query string to parse.
* @returns An object containing the key-value pairs from the query string.
*/
export function parseQueryString(queryString?: string): { [key: string]: string } {
if (!queryString) return {};

return queryString
.slice(1)
.split(‘&’)
.reduce((accumulator, keyValue) => {
const [key, value] = keyValue.split(‘=’);
accumulator[decodeURIComponent(key)] = decodeURIComponent(value);
return accumulator;
}, {} as { [key: string]: string });
}

[FILEPATH] src/decorator-utils.ts [/FILEPATH]
import { ComponentOptionsMixin, VueComponent } from ‘./vue-component’;

/**
* Ensures that the target is a valid Vue component or class component.
* @throw {TypeError} If the target is not a Vue component or class component.
*/
export function ensureIsVueComponent(target: any): asserts target is VueComponent {
if (!isVueComponent(target)) {
throw new TypeError(‘The target must be a Vue component or class component.’);
}
}

/**
* Checks if the given object is a Vue component or class component.
* @param target — The object to check.
* @returns `true` if the target is a Vue component or class component, `false` otherwise.
*/
export function isVueComponent(target: any): target is VueComponent {
return (
typeof target === ‘function’ &&
typeof target.extend === ‘function’ &&
typeof target.mixin === ‘function’ &&
typeof target.use === ‘function’
);
}

/**
* Merges the given Vue component options with the default options.
* @param target — The Vue component to merge the options with.
* @param options — The options to merge.
*/
export function mergeComponentOptions(target: VueComponent, options: ComponentOptionsMixin): void {
Object.assign(target.options || (target.options = {}), options);
}

[FILEPATH] src/debounce.js [/FILEPATH]
/**
* Creates a debounced version of a function.
* @param {Function} func — The function to debounce.
* @param {number} wait — The number of milliseconds to delay.
* @returns {Function} — The debounced version of the function.
*/
export function debounce(func, wait = 0) {
let timeout;

return function(…args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}

[FILEPATH] src/Tween.js [/FILEPATH]
const { pow, sqrt, sin, cos, PI } = Math;

export const Tween = {
Linear: function(k) {
return k;
},
QuadraticIn: function(k) {
return k * k;
},
QuadraticOut: function(k) {
return k * (2 — k);
},
QuadraticInOut: function(k) {
if ((k *= 2) < 1) { return 0.5 * k * k; } return -0.5 * (--k * (k - 2) - 1); }, CubicIn: function(k) { return k * k * k; }, CubicOut: function(k) { return --k * k * k + 1; }, CubicInOut: function(k) { if ((k *= 2) < 1) { return 0.5 * k * k * k; } return 0.5 * ((k -= 2) * k * k + 2); }, QuarticIn: function(k) { return k * k * k * k; }, QuarticOut: function(k) { return 1 - --k * k * k * k; }, QuarticInOut: function(k) { if ((k *= 2) < 1) { return 0.5 * k * k * k * k; } return -0.5 * ((k -= 2) * k * k * k - 2); }, QuinticIn: function(k) { return k * k * k * k * k; }, QuinticOut: function(k) { return --k * k * k * k * k + 1; }, QuinticInOut: function(k) { if ((k *= 2) < 1) { return 0.5 * k * k * k * k * k; } return 0.5 * ((k -= 2) * k * k * k * k + 2); }, SinusoidalIn: function(k) { return 1 - cos(k * PI / 2); }, SinusoidalOut: function(k) { return sin(k * PI / 2); }, SinusoidalInOut: function(k) { return 0.5 * (1 - cos(PI * k)); }, ExponentialIn: function(k) { return k === 0? 0 : pow(1024, k - 1); }, ExponentialOut: function(k) { return k === 1? 1 : 1 - pow(2, -10 * k); }, ExponentialInOut: function(k) { if (k === 0) { return 0; } if (k === 1) { return 1; } if ((k *= 2) < 1) { return 0.5 * pow(1024, k - 1); } return 0.5 * (-pow(2, -10 * (k - 1)) + 2); }, CircularIn: function(k) { return 1 - sqrt(1 - k * k); }, CircularOut: function(k) { return sqrt(1 - --k * k); }, CircularInOut: function(k) { if ((k *= 2) < 1) { return -0.5 * (sqrt(1 - k * k) - 1); } return 0.5 * (sqrt(1 - (k -= 2) * k) + 1); }, ElasticIn: function(k) { if (k === 0) { return 0; } if (k === 1) { return 1; } return -pow(2, 10 * (k - 1)) * sin((k - 1.1) * 6 * PI); }, ElasticOut: function(k) { if (k === 0) { return 0; } if (k === 1) { return 1; } return pow(2, -10 * k) * sin((k - 0.1) * 6 * PI) + 1; }, ElasticInOut: function(k) { if (k === 0) { return 0; } if (k === 1) { return 1; } k *= 2; if (k < 1) { return -0.5 * pow(2, 10 * (k - 1)) * sin((k - 1.1) * 6 * PI); } return 0.5 * pow(2, -10 * (k - 1)) * sin((k - 1.1) * 6 * PI) + 1; }, BackIn: function(k) { const s = 1.70158; return k * k * ((s + 1) * k - s); }, BackOut: function(k) { const s = 1.70158; return --k * k * ((s + 1) * k + s) + 1; }, BackInOut: function(k) { const s = 1.70158 * 1.525; if ((k *= 2) < 1) { return 0.5 * (k * k * ((s + 1) * k - s)); } return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2); }, BounceOut: function(k) { if (k < 1 / 2.75) { return 7.5625 * k * k; } else if (k < 2 / 2.75) { return 7.5625 * (k -= 1.5 / 2.75) * k + 0.75; } else if (k < 2.5 / 2.75) { return 7.5625 * (k -= 2.25 / 2.75) * k + 0.9375; } else { return 7.5625 * (k -= 2.625 / 2.75) * k + 0.984375; } }, BounceIn: function(k) { return 1 - Tween.BounceOut(1 - k); }, BounceInOut: function(k) { if (k < 0.5) { return Tween.BounceIn(k * 2) * 0.5; } return Tween.BounceOut(k * 2 - 1) * 0.5 + 0.5; }, }; [FILEPATH] src/createApp.ts [/FILEPATH] import { VueComponent, isVueComponent } from './vue-component'; import { ensureIsVueComponent, mergeComponentOptions } from './decorator-utils'; import { ComponentOptionsMixin } from './vue-component'; /** * Creates a Vue app with the given options and root component. * @param options - The options for creating the Vue app. * @param rootComponent - The root component of the Vue app. * @returns A function that mounts the Vue app on the specified element. */ export function createApp(options: ComponentOptionsMixin, rootComponent: VueComponent) { ensureIsVueComponent(rootComponent); // Merge the given options with the root component's options mergeComponentOptions(rootComponent, options); // Return a function that mounts the app on the specified element return function mountApp(selector: string | Element): VueComponent { const rootElement = typeof selector === 'string'? document.querySelector(selector) : selector; if (!rootElement) { throw new Error('Invalid selector or element provided for mounting the Vue app.'); } return new rootComponent({ el: rootElement }); }; } [FILEPATH] src/TweenValue.js [/FILEPATH] import { now } from './utils'; import Tween from './Tween'; class TweenValue { constructor(value = 0) { this.value = value; this.targetValue = value; this.startTime = now(); this.duration = 0; this.tweenFunction = Tween.QuadraticInOut; this.onUpdate = null; this.onComplete = null; } get isAnimating() { return this.duration!== 0; } stop() { this.duration = 0; } update() { if (this.duration === 0) { this.value = this.targetValue; this.onComplete && this.onComplete(); return; } const currentTime = now(); const elapsedTime = currentTime - this.startTime; const t = Math.min(1, elapsedTime / this.duration); const easing = this.tweenFunction(t); this.value = this.startValue + (this.targetValue - this.startValue) * easing; this.onUpdate && this.onUpdate(); if (t === 1) { this.duration = 0; this.onComplete && this.onComplete(); } } to(value, duration, tweenFunction = Tween.QuadraticInOut) { this.startValue = this.value; this.targetValue = value; this.startTime = now(); this.duration = duration; this.tweenFunction = tweenFunction; } } export default TweenValue; [FILEPATH] src/BEM.ts [/FILEPATH] export type BEMMods = Record;

const createBEMClass = (blockName: string, elementName?: string, mods?: BEMMods, mix?: Array): string => {
const classNames: string[] = [];

if (elementName) {
classNames.push(`${blockName}__${elementName}`);
} else {
classNames.push(blockName);
}

if (mods) {
Object.keys(mods).forEach((key) => {
if (mods[key]) {
classNames.push(elementName? `${blockName}__${elementName}—${key}` : `${blockName}—${key}`);
}
});
}

if (mix) {
mix.forEach((className) => {
if (className) {
classNames.push(className);
}
});
}

return classNames.join(‘ ‘);
};

export const BEM = {
create: (blockName: string) => {
const block = (elementName?: string, mods?: BEMMods, mix?: Array): string =>
createBEMClass(blockName, elementName, mods, mix);

return block;
},
};

[FILEPATH] src/BaseIndexedContainer.ts [/FILEPATH]
import { ComponentOptionsMixin, VueComponent } from ‘./vue-component’;

/**
* An abstract base class for components that serve as indexed containers
* for child components in a Vue application.
*/
export abstract class BaseIndexedContainer extends VueComponent {
/**
* The unique identifier for the container.
*/
id!: string;

/**
* An array of child components managed by the container.
*/
components: VueComponent[] = [];

/**
* Adds a child component to the container.
* @param component — The child component to add.
*/
abstract addComponent(component: VueComponent): void;

/**
* Removes a child component from the container.
* @param component — The child component to remove.
*/
abstract removeComponent(component: VueComponent): void;

/**
* Retrieves a child component by its index.
* @param index — The index of the child component.
* @returns The child component at the specified index.
*/
abstract getComponentByIndex(index: number): VueComponent | null;

/**
* Retrieve the index of a given component within the container.
* @param component — The component to search for.
* @returns The index of the component, or `-1` if it is not found.
*/
abstract getIndexOfComponent(component: VueComponent): number;
}

/**
* Options for the `BaseIndexedContainer` class.
*/
export interface BaseIndexedContainerOptions extends ComponentOptionsMixin {
id: string;
}

[FILEPATH] src/TouchLayer.js [/FILEPATH]
import { BaseLayer } from ‘./layers/BaseLayer’;
import { Tween } from ‘./Tween’;
import { normalizeArray } from ‘./utils’;

class TouchLayer extends BaseLayer {
constructor(elements) {
super(elements);

this.touched = false;
this.touchStartX = 0;
this.touchStartY = 0;
this.touchDistance = 0;
this.touchDistanceX = 0;
this.touchDistanceY = 0;

this.onTouchStart = this.onTouchStart.bind(this);
this.onTouchMove = this.onTouchMove.bind(this);
this.onTouchEnd = this.onTouchEnd.bind(this);
}

attach() {
this.elements.forEach(el => {
el.addEventListener(‘touchstart’, this.onTouchStart, false);
});
}

detach() {
this.elements.forEach(el => {
el.removeEventListener(‘touchstart’, this.onTouchStart);
});
}

onTouchStart(event) {
if (event.touches.length === 1) {
this.touched = true;
this.touchStartX = event.touches[0].pageX;
this.touchStartY = event.touches[0].pageY;
document.addEventListener(‘touchmove’, this.onTouchMove, false);
document.addEventListener(‘touchend’, this.onTouchEnd, false);
}
}

onTouchMove(event) {
if (this.touched) {
const touch = event.touches[0];
const distanceX = touch.pageX — this.touchStartX;
const distanceY = touch.pageY — this.touchStartY;
this.touchDistance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
this.touchDistanceX = distanceX;
this.touchDistanceY = distanceY;
this.emit(‘touchmove’, { distance: this.touchDistance, distanceX: this.touchDistanceX, distanceY: this.touchDistanceY });
}
}

onTouchEnd(event) {
if (this.touched) {
this.touched = false;
document.removeEventListener(‘touchmove’, this.onTouchMove);
document.removeEventListener(‘touchend’, this.onTouchEnd);

const minimumSwipeDistance = this.options.minimumSwipeDistance || 30;
const maxmimumTapDistance = this.options.maximumTapDistance || 10;
const currentTime = new Date().getTime();
const tapsThreshold = this.options.tapsThreshold || 250;
const swipesThreshold = this.options.swipesThreshold || 500;

if (this.touchDistance < maxmimumTapDistance) { if (currentTime - this.lastTapTime < tapsThreshold) { this.emit('doubletap'); } else { this.emit('tap'); } this.lastTapTime = currentTime; } else if (this.touchDistance > minimumSwipeDistance) {
if (currentTime — this.lastSwipeTime > swipesThreshold) {
if (Math.abs(this.touchDistanceX) > Math.abs(this.touchDistanceY)) {
this.emit(this.touchDistanceX > 0? ‘swiperight’ : ‘swipeleft’);
} else {
this.emit(this.touchDistanceY > 0? ‘swipedown’ : ‘swipeup’);
}
this.lastSwipeTime = currentTime;
}
}
}
}

get lastTapTime() {
return this._lastTapTime || 0;
}

set lastTapTime(value) {
this._lastTapTime = value;

setTimeout(() => {
delete this._lastTapTime;
}, this.options.tapsThreshold || 250);
}

get lastSwipeTime() {
return this._lastSwipeTime || 0;
}

set lastSwipeTime(value) {
this._lastSwipeTime = value;

setTimeout(() => {
delete this._lastSwipeTime;
}, this.options.swipesThreshold || 500);
}
}

TouchLayer.DEFAULT_OPTIONS = {
minimumSwipeDistance: 30,
maximumTapDistance: 10,
tapsThreshold: 250,
swipesThreshold: 500,
};

export default TouchLayer;

[FILEPATH] src/PointerLayer.js [/FILEPATH]
import { BaseLayer } from ‘./layers/BaseLayer’;
import { Tween } from ‘./Tween’;
import { normalizeArray } from ‘./utils’;

class PointerLayer extends BaseLayer {
constructor(elements) {
super(elements);

this.pointered = false;
this.pointerStartX = 0;
this.pointerStartY = 0;
this.pointerDistance = 0;
this.pointerDistanceX = 0;
this.pointerDistanceY = 0;

this.onPointerDown = this.onPointerDown.bind(this);
this.onPointerMove = this.onPointerMove.bind(this);
this.onPointerUp = this.onPointerUp.bind(this);
}

attach() {
this.elements.forEach(el => {
el.addEventListener(‘pointerdown’, this.onPointerDown, false);
});
}

detach() {
this.elements.forEach(el => {
el.removeEventListener(‘pointerdown’, this.onPointerDown);
});
}

onPointerDown(event) {
if (!event.isPrimary) {
return;
}

this.pointered = true;
[FILEPATH] src/SwipeLayer.js [/FILEPATH]
import { BaseLayer } from ‘./BaseLayer’;

class SwipeLayer extends BaseLayer {
constructor(elements) {
super(elements);

this.swiping = false;
this.swipeStartX = 0;
this.swipeStartY = 0;
this.swipeDistance = 0;
this.swipeDistanceX = 0;
this.swipeDistanceY = 0;

this.onMouseDown = this.onMouseDown.bind(this);
this.onMouseMove = this.onMouseMove.bind(this);
this.onMouseUp = this.onMouseUp.bind(this);
}

attach() {
this.elements.forEach(el => {
el.addEventListener(‘mousedown’, this.onMouseDown, false);
});
}

detach() {
this.elements.forEach(el => {
el.removeEventListener(‘mousedown’, this.onMouseDown);
});
}

onMouseDown(event) {
if (event.which!== 1) {
return;
}

this.swiping = true;
this.swipeStartX = event.pageX;
this.swipeStartY = event.pageY;
document.addEventListener(‘mousemove’, this.onMouseMove, false);
document.addEventListener(‘mouseup’, this.onMouseUp, false);
}

onMouseMove(event) {
if (this.swiping) {
const distanceX = event.pageX — this.swipeStartX;
const distanceY = event.pageY — this.swipeStartY;
this.swipeDistance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
this.swipeDistanceX = distanceX;
this.swipeDistanceY = distanceY;
this.emit(‘swipemove’, { distance: this.swipeDistance, distanceX: this.swipeDistanceX, distanceY: this.swipeDistanceY });
}
}

onMouseUp(event) {
if (this.swiping) {
this.swiping = false;
document.removeEventListener(‘mousemove’, this.onMouseMove);
document.removeEventListener(‘mouseup’, this.onMouseUp);

const minimumSwipeDistance = this.options.minimumSwipeDistance || 30;

if (this.swipeDistance > minimumSwipeDistance) {
if (Math.abs(this.swipeDistanceX) > Math.abs(this.swipeDistanceY)) {
this.emit(this.swipeDistanceX > 0? ‘swiperight’ : ‘swipeleft’);
} else {
this.emit(this.swipeDistanceY > 0? ‘swipedown’ : ‘swipeup’);
}
}
}
}
}

SwipeLayer.DEFAULT_OPTIONS = {
minimumSwipeDistance: 30,
};

export default SwipeLayer;

[FILEPATH] src/IndexedElementContainer.ts [/FILEPATH]
import { BaseIndexedContainer, BaseIndexedContainerOptions } from ‘./BaseIndexedContainer’;

/**
* An `IndexedElementContainer` is a concrete implementation of the
* `BaseIndexedContainer` abstract class that manages child components
* as DOM elements.
*/
export class IndexedElementContainer extends BaseIndexedContainer {
/**
* Creates an instance of `IndexedElementContainer`.
* @param options — The options for creating the container.
*/
constructor(options: BaseIndexedContainerOptions) {
super(options);
}

/**
* Adds a child element to the container.
* @param element — The child element to add.
*/
addComponent(element: Element): void {
this.components.push(element);
}

/**
* Removes a child element from the container.
* @param element — The child element to remove.
*/
removeComponent(element: Element): void {
const index = this.components.indexOf(element);
if (index!== -1) {
this.components.splice(index, 1);
}
}

/**
* Retrieves a child element by its index.
* @param index — The index of the child element.
* @returns The child element at the specified index.
*/
getComponentByIndex(index: number): Element | null {
return this.components[index] || null;
}

/**
* Retrieve the index of a given element within the container.
* @param element — The element to search for.
* @returns The index of the element, or `-1` if it is not found.
*/
getIndexOfComponent(element: Element): number {
return this.components.indexOf(element);
}
}

[FILEPATH] src/gesture-listener.test.ts [/FILEPATH]
/*@include util-shim.js*/
/*@include BEM.js*/
let testBody;

QUnit.module(‘GestureListener’, {
beforeEach: function() {
testBody = document.getElementById(‘qunit-fixture’);
},
afterEach: function() {
testBody.innerHTML = »;
},
});

QUnit.test(‘should detect tap and doubletap gestures’, function(assert) {
const done = assert.async();

const container = document.createElement(‘div’);
container.style.width = ‘100px’;
container.style.height = ‘100px’;
testBody.appendChild(container);

const gestureListener = new GestureListener();

gestureListener.on(‘tap’, function(event) {
console.log(‘tap’, event);
assert.equal(event.type, ‘tap’);
});

gestureListener.on(‘doubletap’, function(event) {
console.log(‘doubletap’, event);
assert.equal(event.type, ‘doubletap’);
done();
});

gestureListener.attach();

triggerTouchEvent(container, ‘touchstart’, 50, 50);
triggerTouchEvent(container, ‘touchend’, 50, 50);
setTimeout(function() {
triggerTouchEvent(container, ‘touchstart’, 50, 50);
triggerTouchEvent(container, ‘touchend’, 50, 50);
}, 100);
});

QUnit.test(‘should detect swipe gestures’, function(assert) {
const done = assert.async();

const container = document.createElement(‘div’);
container.style.width = ‘100px’;
container.style.height = ‘100px’;
testBody.appendChild(container);

const gestureListener = new GestureListener();

gestureListener.on(‘swipe_left’, function(event) {
console.log(‘swipe_left’, event);
assert.equal(event.type, ‘swipe_left’);
});

gestureListener.on(‘swipe_right’, function(event) {
console.log(‘swipe_right’, event);
assert.equal(event.type, ‘swipe_right’);
done();
});

gestureListener.attach();

triggerTouchEvent(container, ‘touchstart’, 90, 50);
triggerTouchEvent(container, ‘touchmove’, 10, 50);
triggerTouchEvent(container, ‘touchend’, 10, 50);

setTimeout(function() {
triggerTouchEvent(container, ‘touchstart’, 10, 50);
triggerTouchEvent(container, ‘touchmove’, 90, 50);
triggerTouchEvent(container, ‘touchend’, 90, 50);
}, 100);
});

QUnit.test(‘should detect longpress gesture’, function(assert) {
const done = assert.async();

const container = document.createElement(‘div’);
container.style.width = ‘100px’;
container.style.height = ‘100px’;
testBody.appendChild(container);

const gestureListener = new GestureListener();

gestureListener.on(‘longpress’, function(event) {
console.log(‘longpress’, event);
assert.equal(event.type, ‘longpress’);
done();
});

gestureListener.attach();

triggerTouchEvent(container, ‘touchstart’, 50, 50);
setTimeout(function() {
triggerTouchEvent(container, ‘touchend’, 50, 50);
}, 600);
});

function triggerTouchEvent(element, type, x, y) {
const touchObj = new Touch({
identifier: Date.now(),
target: element,
clientX: x,
clientY: y,
screenX: x,
screenY: y,
pageX: x,
pageY: y,
radiusX: 2.5,
radiusY: 2.5,
rotationAngle: 10,
force: 1,
});

const touchEvent = new TouchEvent(type, {
cancelable: true,
bubbles: true,
touches: [touchObj],
targetTouches: [],
changedTouches: [touchObj],
shiftKey: false,
});

element.dispatchEvent(touchEvent);
}

[FILEPATH] src/types.ts [/FILEPATH]
declare global {
interface TouchInit extends Touch {
identifier: number;
target: EventTarget;
}

var Touch: {
prototype: Touch;
new(touchInitDict: TouchInit): Touch;
};
}

[FILEPATH] src/ClickLayer.test.js [/FILEPATH]
/*@include util-shim.js*/
/*@include ClickLayer.js*/
/*@include BEM.js*/

let testBody;

QUnit.module(‘ClickLayer’, {
beforeEach: function() {
testBody = document.getElementById(‘qunit-fixture’);
},
afterEach: function() {
testBody.innerHTML = »;
},
});

QUnit.test(‘should trigger click event’, function(assert) {
const done = assert.async();

const container = document.createElement(‘div’);
testBody.appendChild(container);

const clickLayer = new ClickLayer(container);

clickLayer.on(‘click’, function(event) {
assert.equal(event.type, ‘click’);
done();
});

clickLayer.attach();

triggerMouseEvent(container, ‘click’);
});

function triggerMouseEvent(element, type) {
const mouseEvent = new MouseEvent(type, {
cancelable: true,
bubbles: true,
view: window,
detail: 1,
screenX: 0,
screenY: 0,
clientX: 0,
clientY: 0,
ctrlKey: false,
altKey: false,
shiftKey: false,
metaKey: false,
button: 0,
buttons: 1,
relatedTarget: null,
});

element.dispatchEvent(mouseEvent);
}

[FILEPATH] src/EventEmitter.test.js [/FILEPATH]
/*@include util-shim.js*/
let testBody;

QUnit.module(‘EventEmitter’, {
beforeEach: function() {
testBody = document.getElementById(‘qunit-fixture’);
},
afterEach: function() {
testBody.innerHTML = »;
},
});

QUnit.test(‘should emit events to listener’, function(assert) {
const eventEmitter = new EventEmitter();
const listener = sinon.spy();

eventEmitter.on(‘test’, listener);
eventEmitter.emit(‘test’, ‘testpayload’);

assert.ok(listener.calledWith(‘testpayload’));
});

QUnit.test(‘should remove listener’, function(assert) {
const eventEmitter = new EventEmitter();
const listener = sinon.spy();

eventEmitter.on(‘test’, listener);
eventEmitter.off(‘test’, listener);
eventEmitter.emit(‘test’, ‘testpayload’);

assert.notOk(listener.calledWith(‘testpayload’));
});

[FILEPATH] src/TouchLayer.js [/FILEPATH]
import { BaseLayer } from ‘./layers/BaseLayer’;
import { Tween } from ‘./Tween’;
import { normalizeArray } from ‘./utils’;

class TouchLayer extends BaseLayer {
constructor(elements) {
super(elements);

this.touched = false;
this.touchStartX = 0;
this.touchStartY = 0;
this.touchDistance = 0;
this.touchDistanceX = 0;
this.touchDistanceY = 0;

this.onTouchStart = this.onTouchStart.bind(this);
this.onTouchMove = this.onTouchMove.bind(this);
this.onTouchEnd = this.onTouchEnd.bind(this);
}

attach() {
this.elements.forEach(el => {
el.addEventListener(‘touchstart’, this.onTouchStart, false);
});
}

detach() {
this.elements.forEach(el => {
el.removeEventListener(‘touchstart’, this.onTouchStart);
});
}

onTouchStart(event) {
if (event.touches.length === 1) {
this.touched = true;
this.touchStartX = event.touches[0].pageX;
this.touchStartY = event.touches[0].pageY;
document.addEventListener(‘touchmove’, this.onTouchMove, false);
document.addEventListener(‘touchend’, this.onTouchEnd, false);
}
}

onTouchMove(event) {
if (this.touched) {
const touch = event.touches[0];
const distanceX = touch.pageX — this.touchStartX;
const distanceY = touch.pageY — this.touchStartY;
this.touchDistance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
this.touchDistanceX = distanceX;
this.touchDistanceY = distanceY;
this.emit(‘touchmove’, { distance: this.touchDistance, distanceX: this.touchDistanceX, distanceY: this.touchDistanceY });
}
}

onTouchEnd(event) {
if (this.touched) {
this.touched = false;
document.removeEventListener(‘touchmove’, this.onTouchMove);
document.removeEventListener(‘touchend’, this.onTouchEnd);

const minimumSwipeDistance = this.options.minimumSwipeDistance || 30;
const maxmimumTapDistance = this.options.maximumTapDistance || 10;
const currentTime = new Date().getTime();
const tapsThreshold = this.options.tapsThreshold || 250;
const swipesThreshold = this.options.swipesThreshold || 500;

if (this.touchDistance < maxmimumTapDistance) { if (currentTime - this.lastTapTime < tapsThreshold) { this.emit('doubletap'); } else { this.emit('tap'); } this.lastTapTime = currentTime; } else if (this.touchDistance > minimumSwipeDistance) {
if (currentTime — this.lastSwipeTime > swipesThreshold) {
if (Math.abs(this.touchDistanceX) > Math.abs(this.touchDistanceY)) {
this.emit(this.touchDistanceX > 0? ‘swiperight’ : ‘swipeleft’);
} else {
this.emit(this.touchDistanceY > 0? ‘swipedown’ : ‘swipeup’);
}
this.lastSwipeTime = currentTime;
}
}
}
}

get lastTapTime() {
return this._lastTapTime || 0;
}

set lastTapTime(value) {
this._lastTapTime = value;

setTimeout(() => {
delete this._lastTapTime;
}, this.options.tapsThreshold || 250);
}

get lastSwipeTime() {
return this._lastSwipeTime || 0;
}

set lastSwipeTime(value) {
this._lastSwipeTime = value;

setTimeout(() => {
delete this._lastSwipeTime;
}, this.options.swipesThreshold || 500);
}
}

TouchLayer.DEFAULT_OPTIONS = {
minimumSwipeDistance: 30,
maximumTapDistance: 10,
tapsThreshold: 250,
swipesThreshold: 500,
};

export default TouchLayer;

[FILEPATH] src/PointerLayer.test.js [/FILEPATH]
/*@include util-shim.js*/
/*@include PointerLayer.js*/
/*@include BEM.js*/

let testBody;

QUnit.module(‘PointerLayer’, {
beforeEach: function() {
testBody = document.getElementById(‘qunit-fixture’);
},
afterEach: function() {
testBody.innerHTML = »;
},
});

QUnit.test(‘should trigger pointerdown event’, function(assert) {
const done = assert.async();

const container = document.createElement(‘div’);
testBody.appendChild(container);

const pointerLayer = new PointerLayer(container);

pointerLayer.on(‘pointerdown’, function(event) {
assert.equal(event.type, ‘pointerdown’);
done();
});

pointerLayer.attach();

triggerPointerEvent(container, ‘pointerdown’);
});

QUnit.test(‘should trigger pointerup event’, function(assert) {
const done = assert.async();

const container = document.createElement(‘div’);
testBody.appendChild(container);

const pointerLayer = new PointerLayer(container);

pointerLayer.on(‘pointerup’, function(event) {
assert.equal(event.type, ‘pointerup’);
done();
});

pointerLayer.attach();

triggerPointerEvent(container, ‘pointerdown’);
triggerPointerEvent(container, ‘pointerup’);
});

QUnit.test(‘should trigger pointermove event’, function(assert) {
const done = assert.async();

const container = document.createElement(‘div’);
testBody.appendChild(container);

const pointerLayer = new PointerLayer(container);

pointerLayer.on(‘pointermove’, function(event) {
assert.equal(event.type, ‘pointermove’);
done();
});

pointerLayer.attach();

triggerPointerEvent(container, ‘pointerdown’);
triggerPointerEvent(container, ‘pointermove’);
});

function triggerPointerEvent(element, type) {
const pointerEvent = new PointerEvent(type, {
bubbles: true,
cancelable: true,
view: window,
detail: 1,
screenX: 0,
screenY: 0,
clientX: 0,
clientY: 0,
ctrlKey: false,
altKey: false,
shiftKey: false,
metaKey: false,
button: 0,
buttons: 1,
relatedTarget: null,
pointerId: 1,
pointerType: ‘mouse’,
isPrimary: true,
width: 1,
height: 1,
pressure: 1,
tiltX: 0,
tiltY: 0,
});

element.dispatchEvent(pointerEvent);
}

[FILEPATH] src/SwipeLayer.test.js [/FILEPATH]
/*@include util-shim.js*/
/*@include SwipeLayer.js*/
/*@include BEM.js*/

let testBody;

QUnit.module(‘SwipeLayer’, {
beforeEach: function() {
testBody = document.getElementById(‘qunit-fixture’);
},
afterEach: function() {
testBody.innerHTML = »;
},
});

QUnit.test(‘should trigger swipe events’, function(assert) {
const done = assert.async(4);

const container = document.createElement(‘div’);
testBody.appendChild(container);

const swipeLayer = new SwipeLayer(container);

swipeLayer.on(‘swipeleft’, function(event) {
assert.equal(event.type, ‘swipeleft’);
done();
});

swipeLayer.on(‘swiperight’, function(event) {
assert.equal(event.type, ‘swiperight’);
done();
});

swipeLayer.on(‘swipeup’, function(event) {
assert.equal(event.type, ‘swipeup’);
done();
});

swipeLayer.on(‘swipedown’, function(event) {
assert.equal(event.type, ‘swipedown’);
done();
});

swipeLayer.attach();

triggerMouseEvent(container, ‘mousedown’, 50, 50);
triggerMouseEvent(container, ‘mousemove’, 0, 50);
triggerMouseEvent(container, ‘mouseup’, 0, 50);

triggerMouseEvent(container, ‘mousedown’, 50, 50);
triggerMouseEvent(container, ‘mousemove’, 100, 50);
triggerMouseEvent(container, ‘mouseup’, 100, 50);

triggerMouseEvent(container, ‘mousedown’, 50, 50);
triggerMouseEvent(container, ‘mousemove’, 50, 0);
triggerMouseEvent(container, ‘mouseup’, 50, 0);

triggerMouseEvent(container, ‘mousedown’, 50, 50);
triggerMouseEvent(container, ‘mousemove’, 50, 100);
triggerMouseEvent(container, ‘mouseup’, 50, 100);
});

function triggerMouseEvent(element, type, x, y) {
const mouseEvent = new MouseEvent(type, {
cancelable: type === ‘mousemove’? false : true,
bubbles: true,
view: window,
detail: 1,
screenX: x,
screenY: y,
clientX: x,
clientY: y,
ctrlKey: false,
altKey: false,
shiftKey: false,
metaKey: false,
button: 0,
buttons: type === ‘mousemove’? 1 : 0,
relatedTarget: null,
});

element.dispatchEvent(mouseEvent);
}

[FILEPATH] src/ClickLayer.js [/FILEPATH]
import { BaseLayer } from ‘./layers/BaseLayer’;

class ClickLayer extends BaseLayer {
constructor(elements) {
super(elements);

this.onClick = this.onClick.bind(this);
}

attach() {
this.elements.forEach(el => {
el.addEventListener(‘click’, this.onClick, false);
});
}

detach() {
this.elements.forEach(el => {
el.removeEventListener(‘click’, this.onClick);
});
}

onClick(event) {
this.emit(‘click’, event);
}
}

export default ClickLayer;

[FILEPATH] src/MouseLayer.test.js [/FILEPATH]
/*@include util-shim.js*/
/*@include MouseLayer.js*/
/*@include BEM.js*/

let testBody;

QUnit.module(‘MouseLayer’, {
beforeEach: function() {
testBody = document.getElementById(‘qunit-fixture’);
},
afterEach: function() {
testBody.innerHTML = »;
},
});

QUnit.test(‘should trigger mouse events’, function(assert) {
const done = assert.async(3);

const container = document.createElement(‘div’);
testBody.appendChild(container);

const mouseLayer = new MouseLayer(container);

mouseLayer.on(‘mouseover’, function(event) {
assert.equal(event.type, ‘mouseover’);
done();
});

mouseLayer.on(‘mouseout’, function(event) {
assert.equal(event.type, ‘mouseout’);
done();
});

mouseLayer.on(‘click’, function(event) {
assert.equal(event.type, ‘click’);
done();
});

mouseLayer.attach();

triggerMouseEvent(container, ‘mouseover’);
triggerMouseEvent(container, ‘mouseout’);
triggerMouseEvent(container, ‘click’);
});

function triggerMouseEvent(element, type) {
const mouseEvent = new MouseEvent(type, {
cancelable: true,
bubbles: true,
view: window,
detail: 1,
screenX: 0,
screenY: 0,
clientX: 0,
clientY: 0,
ctrlKey: false,
altKey: false,
shiftKey: false,
metaKey: false,
button: 0,
buttons: 1,
relatedTarget: null,
});

element.dispatchEvent(mouseEvent);
}

[FILEPATH] src/MouseLayer.js [/FILEPATH]
import { BaseLayer } from ‘./layers/BaseLayer’;

class MouseLayer extends BaseLayer {
constructor(elements) {
super(elements);

this.onMouseOver = this.onMouseOver.bind(this);
this.onMouseOut = this.onMouseOut.bind(this);
this.onMouseDown = this.onMouseDown.bind(this);
this.onMouseUp = this.onMouseUp.bind(this);
this.onMouseMove = this.onMouseMove.bind(this);
this.onClick = this.onClick.bind(this);
}

attach() {
this.elements.forEach(el => {
el.addEventListener(‘mouseover’, this.onMouseOver, false);
el.addEventListener(‘mouseout’, this.onMouseOut, false);
el.addEventListener(‘mousedown’, this.onMouseDown, false);
el.addEventListener(‘mouseup’, this.onMouseUp, false);
el.addEventListener(‘mousemove’, this.onMouseMove, false);
el.addEventListener(‘click’, this.onClick, false);
});
}

detach() {
this.elements.forEach(el => {
el.removeEventListener(‘mouseover’, this.onMouseOver);
el.removeEventListener(‘mouseout’, this.onMouseOut);
el.removeEventListener(‘mousedown’, this.onMouseDown);
el.removeEventListener(‘mouseup’, this.onMouseUp);
el.removeEventListener(‘mousemove’, this.onMouseMove);
el.removeEventListener(‘click’, this.onClick);
});
}

onMouseOver(event) {
this.emit(‘mouseover’, event);
}

onMouseOut(event) {
this.emit(‘mouseout’, event);
}

onMouseDown(event) {
this.emit(‘mousedown’, event);
}

onMouseUp(event) {
this.emit(‘mouseup’, event);
}

onMouseMove(event) {
this.emit(‘mousemove’, event);
}

onClick(event) {
this.emit(‘click’, event);
}
}

export default MouseLayer;

[FILEPATH] src/KeyboardLayer.test.js [/FILEPATH]
/*@include util-shim.js*/
/*@include KeyboardLayer.js*/
/*@include BEM.js*/

let testBody;

QUnit.module(‘KeyboardLayer’, {
beforeEach: function() {
testBody = document.getElementById(‘qunit-fixture’);
},
afterEach: function() {
testBody.innerHTML = »;
},
});

QUnit.test(‘should trigger keyboard events’, function(assert) {
const done = assert.async(3);

const container = document.createElement(‘div’);
container.setAttribute(‘tabindex’, ‘0’);
testBody.appendChild(container);

const keyboardLayer = new KeyboardLayer(container);

keyboardLayer.on(‘keydown’, function(event) {
assert.equal(event.type, ‘keydown’);
done();
});

keyboardLayer.on(‘keyup’, function(event) {
assert.equal(event.type, ‘keyup’);
done();
});

keyboardLayer.on(‘keypress’, function(event) {
assert.equal(event.type, ‘keypress’);
done();
});

keyboardLayer.attach();

triggerKeyboardEvent(container, ‘keydown’, ‘a’);
triggerKeyboardEvent(container, ‘keyup’, ‘a’);
triggerKeyboardEvent(container, ‘keypress’, ‘a’);
});

function triggerKeyboardEvent(element, type, key) {
const keyboardEvent = new KeyboardEvent(type, {
cancelable: type === ‘keydown’? false : true,
bubbles: true,
view: window,
key: key,
code: key.charCodeAt(0),
location: 0,
ctrlKey: false,
altKey: false,
shiftKey: false,
metaKey: false,
repeat: false,
isComposing: false,
charCode: 0,
keyCode: 0,
});

element.dispatchEvent(keyboardEvent);
}

[FILEPATH] src/EventEmitter.js [/FILEPATH]
class EventEmitter {
constructor() {
this._events = {};
}

on(type, listener) {
const events = this._events;

if (events[type] === undefined) {
events[type] = [];
}

events[type].push(listener);
}

off(type, listener) {
const events = this._events;

if (events[type]!== undefined) {
const index = events[type].indexOf(listener);

if (index!== -1) {
events[type].splice(index, 1);
}
}
}

emit(type, event) {
const events = this._events;

if (events[type]!== undefined) {
const listeners = events[type].slice();

for (
let i = 0, len = listeners.length;
i < len; i++ ) { listeners[i].apply(this, [event]); } } } } export default EventEmitter; [FILEPATH] src/KeyboardLayer.js [/FILEPATH] import { BaseLayer } from './layers/BaseLayer'; class KeyboardLayer extends BaseLayer { constructor(elements) { super(elements); this.onKeyDown = this.onKeyDown.bind(this); this.onKeyUp = this.onKeyUp.bind(this); this.onKeyPress = this.onKeyPress.bind(this); } attach() { this.elements.forEach(el => {
el.addEventListener(‘keydown’, this.onKeyDown, false);
el.addEventListener(‘keyup’, this.onKeyUp, false);
el.addEventListener(‘keypress’, this.onKeyPress, false);
});
}

detach() {
this.elements.forEach(el => {
el.removeEventListener(‘keydown’, this.onKeyDown);
el.removeEventListener(‘keyup’, this.onKeyUp);
el.removeEventListener(‘keypress’, this.onKeyPress);
});
}

onKeyDown(event) {
this.emit(‘keydown’, event);
}

onKeyUp(event) {
this.emit(‘keyup’, event);
}

onKeyPress(event) {
this.emit(‘keypress’, event);
}
}

export default KeyboardLayer;

[FILEPATH] src/EventEmitterMixin.js [/FILEPATH]
import { EventEmitter } from ‘./EventEmitter’;

const EventEmitterMixin = {
created: function() {
this.$eventEmitter = new EventEmitter();
},
beforeDestroy: function() {
this.$eventEmitter.off();
this.$eventEmitter = null;
},
methods: {
$on: function(type, listener) {
this.$eventEmitter.on(type, listener);
},
$off: function(type, listener) {
this.$eventEmitter.off(type, listener);
},
$emit: function(type, event) {
this.$eventEmitter.emit(type, event);
},
},
};

export default EventEmitterMixin;

[FILEPATH] src/gesture-listener.ts [/FILEPATH]
import { BaseListener } from ‘./BaseListener’;
import TypedEventEmitter from ‘./TypedEventEmitter’;

export declare type TouchEvent = ‘touchstart’ | ‘touchend’ | ‘touchmove’ | ‘touchcancel’;
export declare type GestureEvent =
| ‘pinch’
| ‘rotate’
| ‘swipe_left’
| ‘swipe_right’
| ‘swipe_up’
| ‘swipe_down’
| ‘longpress’
| ‘tap’;

export interface TouchedPoint {
identifier: number;
pageX: number;
pageY: number;
}

export interface Gesture {
type: GestureEvent;
targetTouches: TouchedPoint[];
changedTouches: TouchedPoint[];
timeStamp: DOMHighResTimeStamp;
}

export interface GestureOptions {
tapsThreshold: number;
longPressThreshold: number;
swipeMaxDeviation: number;
swipeMinDistance: number;
}

export declare type GestureEventHandler = (gesture: Gesture) => void;
export declare type TouchEventHandler = (event: TouchEvent) => void;

/**
* The `GestureListener` class enhances the base `EventListener` class
* to detect multi-touch gestures, such as pinch, rotate, swipe, etc.,
* and emits the corresponding custom events.
* @extends TypedEventEmitter
*/
export class GestureListener extends TypedEventEmitter {
private gestureOptions: GestureOptions;
private touchPoints: { [key: string]: TouchedPoint };
private lastTouchEndTime: number;

/**
* Creates a new instance of `GestureListener` with the given options.
*/
public constructor();
public constructor(options?: Partial) {
super();

this.gestureOptions = {
tapsThreshold: 250,
longPressThreshold: 500,
swipeMaxDeviation: 50,
swipeMinDistance: 75,
…options,
};

this.touchPoints = {};
this.lastTouchEndTime = 0;

this.attachTouchHandler(‘touchstart’);
this.attachTouchHandler(‘touchend’);
this.attachTouchHandler(‘touchmove’);
this.attachTouchHandler(‘touchcancel’);
}

private attachTouchHandler(type: TouchEvent) {
const target = document.body;

target.addEventListener(type, (event: TouchEvent) => {
this.onTouchEvent(event);
});
}

public attach(): void {
this.attachTouchHandler(‘touchstart’);
this.attachTouchHandler(‘touchend’);
this.attachTouchHandler(‘touchmove’);
this.attachTouchHandler(‘touchcancel’);
}

public detach(): void {
// @TODOImplement detach() method.
}

private onTouchEvent(event: TouchEvent): void {
const { type, targetTouches, changedTouches, timeStamp } = event;
const timeSinceLastTouchEnd = timeStamp — this.lastTouchEndTime;

switch (type) {
case ‘touchstart’:
for (let i = 0; i < targetTouches.length; i++) { const touch = targetTouches[i]; this.touchPoints[touch.identifier] = touch; } if (changedTouches.length === 1 && timeSinceLastTouchEnd < this.gestureOptions.tapsThreshold) { this.emit('doubletap', { type: 'doubletap', targetTouches, changedTouches, timeStamp }); } break; case 'touchend': this.lastTouchEndTime = timeStamp; if (Object.keys(this.touchPoints).length === 1) { const touch = changedTouches[0]; const startPosition = this.touchPoints[touch.identifier]; const distanceX = Math.abs(touch.pageX - startPosition.pageX); const distanceY = Math.abs(touch.pageY - startPosition.pageY); if (distanceX < this.gestureOptions.swipeMinDistance && distanceY < this.gestureOptions.swipeMinDistance) { if (timeSinceLastTouchEnd < this.gestureOptions.longPressThreshold) { this.emit('tap', { type: 'tap', targetTouches, changedTouches, timeStamp }); } else { this.emit('longpress', { type: 'longpress', targetTouches, changedTouches, timeStamp }); } } else if (distanceX > this.gestureOptions.swipeMinDistance && distanceY < this.gestureOptions.swipeMaxDeviation) { const direction = touch.pageX - startPosition.pageX > 0? ‘right’ : ‘left’;
this.emit(`swipe_${direction}` as GestureEvent, { type: `swipe_${direction}` as GestureEvent, targetTouches, changedTouches, timeStamp });
} else if (distanceY > this.gestureOptions.swipeMinDistance && distanceX < this.gestureOptions.swipeMaxDeviation) { const direction = touch.pageY - startPosition.pageY > 0? ‘down’ : ‘up’;
this.emit(`swipe_${direction}` as GestureEvent, { type: `swipe_${direction}` as GestureEvent, targetTouches, changedTouches, timeStamp });
}
}
// fallthrough
case ‘touchcancel’:
for (let i = 0; i < changedTouches.length; i++) { const touch = changedTouches[i]; delete this.touchPoints[touch.identifier]; } break; case 'touchmove': if (targetTouches.length === 2) { const touch1 = targetTouches[0]; const touch2 = targetTouches[1]; const startPosition1 = this.touchPoints[touch1.identifier]; const startPosition2 = this.touchPoints[touch2.identifier]; const distanceX = touch2.pageX - touch1.pageX; const distanceY = touch2.pageY - touch1.pageY; const startDistanceX = startPosition2.pageX - startPosition1.pageX; const startDistanceY = startPosition2.pageY - startPosition1.pageY; const scale = Math.sqrt((distanceX * distanceX + distanceY * distanceY) / (startDistanceX * startDistanceX + startDistanceY * startDistanceY)); if (scale < 1) { this.emit('pinch', { type: 'pinch', targetTouches, changedTouches, timeStamp }); } else { this.emit('spread', { type: 'spread', targetTouches, changedTouches, timeStamp }); } const angle = (Math.atan2(distanceY, distanceX) - Math.atan2(startDistanceY, startDistanceX)) * 180 / Math.PI; if (Math.abs(angle) > 5) {
this.emit(‘rotate’, { type: ‘rotate’, targetTouches, changedTouches, timeStamp });
}
}
break;
}
}
}
[FILEPATH] src/EventManagerLayer.js [/FILEPATH]
import { BaseLayer } from ‘./layers/BaseLayer’;

class EventManagerLayer extends BaseLayer {
constructor(elements) {
super(elements);

this.onMouseDown = this.onMouseDown.bind(this);
this.onMouseUp = this.onMouseUp.bind(this);
this.onMouseOver = this.onMouseOver.bind(this);
this.onMouseOut = this.onMouseOut.bind(this);
this.onMouseMove = this.onMouseMove.bind(this);
this.onClick = this.onClick.bind(this);
this.onDblClick = this.onDblClick.bind(this);
this.onContextMenu = this.onContextMenu.bind(this);
this.onWheel = this.onWheel.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
this.onKeyUp = this.onKeyUp.bind(this);
this.onKeyPress = this.onKeyPress.bind(this);
this.onTouchStart = this.onTouchStart.bind(this);
this.onTouchMove = this.onTouchMove.bind(this);
this.onTouchEnd = this.onTouchEnd.bind(this);
this.onTouchCancel = this.onTouchCancel.bind(this);
this.onPointerDown = this.onPointerDown.bind(this);
this.onPointerMove = this.onPointerMove.bind(this);
this.onPointerUp = this.onPointerUp.bind(this);
this.onPointerCancel = this.onPointerCancel.bind(this);
this.onPointerOver = this.onPointerOver.bind(this);
this.onPointerOut = this.onPointerOut.bind(this);
this.onPointerEnter = this.onPointerEnter.bind(this);
this.onPointerLeave = this.onPointerLeave.bind(this);
this.onGotPointerCapture = this.onGotPointerCapture.bind(this);
this.onLostPointerCapture = this.onLostPointerCapture.bind(this);
}

attach() {
this.elements.forEach(el => {
el.addEventListener(‘mousedown’, this.onMouseDown, false);
el.addEventListener(‘mouseup’, this.onMouseUp, false);
el.addEventListener(‘mouseover’, this.onMouseOver, false);
el.addEventListener(‘mouseout’, this.onMouseOut, false);
el.addEventListener(‘mousemove’, this.onMouseMove, false);
el.addEventListener(‘click’, this.onClick, false);
el.addEventListener(‘dblclick’, this.onDblClick, false);
el.addEventListener(‘contextmenu’, this.onContextMenu, false);
el.addEventListener(‘wheel’, this.onWheel, false);
el.addEventListener(‘keydown’, this.onKeyDown, false);
el.addEventListener(‘keyup’, this.onKeyUp, false);
el.addEventListener(‘keypress’, this.onKeyPress, false);
el.addEventListener(‘touchstart’, this.onTouchStart, false);
el.addEventListener(‘touchmove’, this.onTouchMove, false);
el.addEventListener(‘touchend’, this.onTouchEnd, false);
el.addEventListener(‘touchcancel’, this.onTouchCancel, false);
el.addEventListener(‘pointerdown’, this.onPointerDown, false);
el.addEventListener(‘pointermove’, this.onPointerMove, false);
el.addEventListener(‘pointerup’, this.onPointerUp, false);
el.addEventListener(‘pointercancel’, this.onPointerCancel, false);
el.addEventListener(‘pointerover’, this.onPointerOver, false);
el.addEventListener(‘pointerout’, this.onPointerOut, false);
el.addEventListener(‘pointerenter’, this.onPointerEnter, false);
el.addEventListener(‘pointerleave’, this.onPointerLeave, false);
el.addEventListener(‘gotpointercapture’, this.onGotPointerCapture, false);
el.addEventListener(‘lostpointercapture’, this.onLostPointerCapture, false);
});
}

detach() {
this.elements.forEach(el => {
el.removeEventListener(‘mousedown’, this.onMouseDown);
el.removeEventListener(‘mouseup’, this.onMouseUp);
el.removeEventListener(‘mouseover’, this.onMouseOver);
el.removeEventListener(‘mouseout’, this.onMouseOut);
el.removeEventListener(‘mousemove’, this.onMouseMove);
el.removeEventListener(‘click’, this.onClick);
el.removeEventListener(‘dblclick’, this.onDblClick);
el.removeEventListener(‘contextmenu’, this.onContextMenu);
el.removeEventListener(‘wheel’, this.onWheel);
el.removeEventListener(‘keydown’, this.onKeyDown);
el.removeEventListener(‘keyup’, this.onKeyUp);
el.removeEventListener(‘keypress’, this.onKeyPress);
el.removeEventListener(‘touchstart’, this.onTouchStart);
el.removeEventListener(‘touchmove’, this.onTouchMove);
el.removeEventListener(‘touchend’, this.onTouchEnd);
el.removeEventListener(‘touchcancel’, this.onTouchCancel);
el.removeEventListener(‘pointerdown’, this.onPointerDown);
el.removeEventListener(‘pointermove’, this.onPointerMove);
el.removeEventListener(‘pointerup’, this.onPointerUp);
el.removeEventListener(‘pointercancel’, this.onPointerCancel);
el.removeEventListener(‘pointerover’, this.onPointerOver);
el.removeEventListener(‘pointerout’, this.onPointerOut);
el.removeEventListener(‘pointerenter’, this.onPointerEnter);
appendToLink: function(element) {
const iconElement = this.iconContainer;
this.iconContainer = null;
element.appendChild(iconElement);
},
addOverlay: function() {
if(this.options.overlay){

//Explictly destroy previous overlay
this.destroyOverlay();

let overlay = document.createElement(«div»);
overlay.style.position = «fixed»;
overlay.style.top = «0px»;
overlay.style.left = «0px»;
overlay.style.width = «100%»;
overlay.style.height = «100%»;
overlay.style.zIndex = «9000»;
overlay.style.backgroundColor = «rgba(0,0,0,0.3)»;

if (this.options.overlayCloseOnClick) {
const self = this;
addEvent(overlay, «click», function() {
self.close();
});
}

document.body.appendChild(overlay);
this.overlay = overlay;
}
},
destroyOverlay: function() {
let overlay = this.overlay;
if (overlay!== null) {
overlay.parentNode.removeChild(overlay);
}
},
determineSidesToShow: function() {
if (this.options.sideToPrefer === «initial») {
if (window.matchMedia(«(prefers-color-scheme: dark)»).matches) {
this.sidesToShow = this.options.sides || [«initial»];
} else {
this.sidesToShow = this.options.sides;
}
} else {
this.sidesToShow = this.options.sides || [this.options.sideToPrefer];
}
},
.SEVERAL_FOR_EACHoczR: function(.ARG0) {
.ARG0.forEach(function(color, index) {
const colorName = Object.keys(color)[0];
if (!icons[colorName]) {
icons[colorName] = this.createIcon();
this.container.appendChild(icons[colorName]);
mask.push(colorName);
}
this.setImg(icons[colorName], color[colorName]);
this.setIconStyle(icons[colorName], colorName);
}, this);
},
handleGetParameters: function(parameters) {
if (parameters.m) {
this.setColor(parameters.m);
}
},
setColor: function(color) {
if (color === «no-preference») {
this.options.sideToPrefer = «initial»;
} else {
this.options.sideToPrefer = color;
}
this.determineSidesToShow();

while (this.container.firstChild) {
this.container.removeChild(this.container.firstChild);
}
const self = this;

.SEVERAL_FOR_EACHoczR(this.sidesToShow);
},
createIcon: function() {
const icon = document.createElement(«a»);
icon.style.display = «inline-block»;
icon.style.textDecoration = «none»;
icon.style.outline = «none»;
return icon;
},
setIconStyle: function(icon, color) {
if (color === «initial») {
icon.style.filter = «invert(1)»;
} else {
icon.style.filter = «»;
}
},
setImg: function(iconElement, src) {
let icon = this.options.icon || {};
let img = new Image();
img.style.width = icon.width? icon.width : «20px»;
img.style.height = icon.height? icon.height : «20px»;
img.src = src;
img.style.display = «inline-block»;
img.style pointerEvents = «none»;

if (this.options.title) {
img.title = this.options.title;
}

iconElement.appendChild(img);

if (this.srcImage) {
this.srcImage = null;
}

this.srcImage = src;
},
handleDocumentKeyDown: function(event) {
if (event.keyCode === this.options.keyCode) {
event.preventDefault();
this.toggle();
}
}
});
window.fxy[«Side

Оцените статью
Строительный Эксперт - inhomes.ru
Добавить комментарий