framework7
Version:
Full featured mobile HTML framework for building iOS & Android apps
214 lines (199 loc) • 6.91 kB
JavaScript
import $ from 'dom7';
import Utils from '../../utils/utils';
const Fab = {
morphOpen(fabEl, targetEl) {
const app = this;
const $fabEl = $(fabEl);
const $targetEl = $(targetEl);
if ($targetEl.length === 0) return;
$targetEl.transition(0).addClass('fab-morph-target-visible');
const target = {
width: $targetEl[0].offsetWidth,
height: $targetEl[0].offsetHeight,
offset: $targetEl.offset(),
borderRadius: $targetEl.css('border-radius'),
zIndex: $targetEl.css('z-index'),
};
const fab = {
width: $fabEl[0].offsetWidth,
height: $fabEl[0].offsetHeight,
offset: $fabEl.offset(),
translateX: Utils.getTranslate($fabEl[0], 'x'),
translateY: Utils.getTranslate($fabEl[0], 'y'),
};
$fabEl[0].f7FabMorphData = {
$targetEl,
target,
fab,
};
const diffX = (fab.offset.left + (fab.width / 2))
- (target.offset.left + (target.width / 2))
- fab.translateX;
const diffY = (fab.offset.top + (fab.height / 2))
- (target.offset.top + (target.height / 2))
- fab.translateY;
const scaleX = target.width / fab.width;
const scaleY = target.height / fab.height;
let borderRadius = Math.ceil(parseInt(target.borderRadius, 10) / Math.max(scaleX, scaleY));
if (borderRadius > 0) borderRadius += 2;
$fabEl[0].f7FabMorphResizeHandler = function resizeHandler() {
$fabEl.transition(0).transform('');
$targetEl.transition(0);
target.width = $targetEl[0].offsetWidth;
target.height = $targetEl[0].offsetHeight;
target.offset = $targetEl.offset();
fab.offset = $fabEl.offset();
const diffXNew = (fab.offset.left + (fab.width / 2))
- (target.offset.left + (target.width / 2))
- fab.translateX;
const diffYNew = (fab.offset.top + (fab.height / 2))
- (target.offset.top + (target.height / 2))
- fab.translateY;
const scaleXNew = target.width / fab.width;
const scaleYNew = target.height / fab.height;
$fabEl.transform(`translate3d(${-diffXNew}px, ${-diffYNew}px, 0) scale(${scaleXNew}, ${scaleYNew})`);
};
$targetEl
.css('opacity', 0)
.transform(`scale(${1 / scaleX}, ${1 / scaleY})`);
$fabEl
.addClass('fab-opened')
.css('z-index', target.zIndex - 1)
.transform(`translate3d(${-diffX}px, ${-diffY}px, 0)`);
$fabEl.transitionEnd(() => {
$targetEl.transition('');
Utils.nextFrame(() => {
$targetEl.css('opacity', 1).transform('scale(1,1)');
$fabEl
.transform(`translate3d(${-diffX}px, ${-diffY}px, 0) scale(${scaleX}, ${scaleY})`)
.css('border-radius', `${borderRadius}px`)
.css('box-shadow', 'none')
.css('opacity', '0');
});
app.on('resize', $fabEl[0].f7FabMorphResizeHandler);
if ($targetEl.parents('.page-content').length > 0) {
$targetEl.parents('.page-content').on('scroll', $fabEl[0].f7FabMorphResizeHandler);
}
});
},
morphClose(fabEl) {
const app = this;
const $fabEl = $(fabEl);
const morphData = $fabEl[0].f7FabMorphData;
if (!morphData) return;
const { $targetEl, target, fab } = morphData;
if ($targetEl.length === 0) return;
const diffX = (fab.offset.left + (fab.width / 2))
- (target.offset.left + (target.width / 2))
- fab.translateX;
const diffY = (fab.offset.top + (fab.height / 2))
- (target.offset.top + (target.height / 2))
- fab.translateY;
const scaleX = target.width / fab.width;
const scaleY = target.height / fab.height;
app.off('resize', $fabEl[0].f7FabMorphResizeHandler);
if ($targetEl.parents('.page-content').length > 0) {
$targetEl.parents('.page-content').off('scroll', $fabEl[0].f7FabMorphResizeHandler);
}
$targetEl
.css('opacity', 0)
.transform(`scale(${1 / scaleX}, ${1 / scaleY})`);
$fabEl
.transition('')
.css('box-shadow', '')
.css('border-radius', '')
.css('opacity', '1')
.transform(`translate3d(${-diffX}px, ${-diffY}px, 0)`);
$fabEl.transitionEnd(() => {
$fabEl
.css('z-index', '')
.removeClass('fab-opened')
.transform('');
Utils.nextFrame(() => {
$fabEl.transitionEnd(() => {
$targetEl
.removeClass('fab-morph-target-visible')
.css('opacity', '')
.transform('')
.transition('');
});
});
});
},
open(fabEl, targetEl) {
const app = this;
const $fabEl = $(fabEl).eq(0);
const $buttonsEl = $fabEl.find('.fab-buttons');
if (!$fabEl.length) return;
if ($fabEl.hasClass('fab-opened')) return;
if (!$buttonsEl.length && !$fabEl.hasClass('fab-morph')) return;
if (app.fab.openedEl) {
if (app.fab.openedEl === $fabEl[0]) return;
app.fab.close(app.fab.openedEl);
}
app.fab.openedEl = $fabEl[0];
if ($fabEl.hasClass('fab-morph')) {
app.fab.morphOpen($fabEl, targetEl || $fabEl.attr('data-morph-to'));
} else {
$fabEl.addClass('fab-opened');
}
$fabEl.siblings('.fab-backdrop').addClass('backdrop-in');
$fabEl.trigger('fab:open');
},
close(fabEl = '.fab-opened') {
const app = this;
const $fabEl = $(fabEl).eq(0);
const $buttonsEl = $fabEl.find('.fab-buttons');
if (!$fabEl.length) return;
if (!$fabEl.hasClass('fab-opened')) return;
if (!$buttonsEl.length && !$fabEl.hasClass('fab-morph')) return;
app.fab.openedEl = null;
if ($fabEl.hasClass('fab-morph')) {
app.fab.morphClose($fabEl);
} else {
$fabEl.removeClass('fab-opened');
}
$fabEl.siblings('.fab-backdrop').removeClass('backdrop-in');
$fabEl.trigger('fab:close');
},
toggle(fabEl) {
const app = this;
const $fabEl = $(fabEl);
if (!$fabEl.hasClass('fab-opened')) app.fab.open(fabEl);
else app.fab.close(fabEl);
},
};
export default {
name: 'fab',
create() {
const app = this;
Utils.extend(app, {
fab: {
openedEl: null,
morphOpen: Fab.morphOpen.bind(app),
morphClose: Fab.morphClose.bind(app),
open: Fab.open.bind(app),
close: Fab.close.bind(app),
toggle: Fab.toggle.bind(app),
},
});
},
clicks: {
'.fab > a': function open($clickedEl) {
const app = this;
app.fab.toggle($clickedEl.parents('.fab'));
},
'.fab-open': function open($clickedEl, data = {}) {
const app = this;
app.fab.open(data.fab);
},
'.fab-close': function close($clickedEl, data = {}) {
const app = this;
app.fab.close(data.fab);
},
'.fab-backdrop': function close() {
const app = this;
app.fab.close();
},
},
};