@nativescript-community/ui-neumorphiclayout
Version:
Neumorphism-based layout for NativeScript
176 lines • 7.76 kB
JavaScript
import { backgroundInternalProperty, borderTopColorProperty, borderTopLeftRadiusProperty, borderTopWidthProperty, Color, LayoutBase, Length, Screen, Utils } from '@nativescript/core';
import { darkShadowColorProperty, lightShadowColorProperty, NeumorphicCanvas, NeumorphicType, neumorphismProperty, shadowDistanceProperty, shadowRadiusProperty } from './common';
export * from './common';
const layerNames = ['backgroundDrawable', 'foregroundDrawable'];
var CANeumorphicLayer = /** @class */ (function (_super) {
__extends(CANeumorphicLayer, _super);
function CANeumorphicLayer() {
return _super !== null && _super.apply(this, arguments) || this;
}
CANeumorphicLayer.initWithCanvas = function (canvas) {
var layer = CANeumorphicLayer.layer();
layer.mCanvasRef = new WeakRef(canvas);
return layer;
};
CANeumorphicLayer.prototype.drawInContext = function (ctx) {
var canvas = this.mCanvasRef && this.mCanvasRef.get();
if (canvas != null) {
var size = this.bounds.size;
canvas.setContext(ctx, size.width, size.height);
canvas.onDraw();
}
_super.prototype.drawInContext.call(this, ctx);
};
CANeumorphicLayer.prototype.getAugmentedCanvas = function () {
return this.mCanvasRef && this.mCanvasRef.get();
};
return CANeumorphicLayer;
}(CALayer));
function _getDrawableLayers(view) {
const drawableLayers = [];
const sublayers = view.nativeViewProtected?.layer?.sublayers;
if (sublayers != null) {
const count = sublayers.count;
for (let i = 0, length = sublayers.count; i < length && drawableLayers.length < layerNames.length; i++) {
const layer = sublayers.objectAtIndex(i);
if (layerNames.includes(layer.name)) {
drawableLayers.push(layer);
}
}
}
return drawableLayers;
}
function _refresh() {
const drawableLayers = _getDrawableLayers(this);
if (drawableLayers.length) {
_updateSublayerShadows(this, drawableLayers);
}
}
function _updateSublayerBounds(view) {
// Check this for cases like when view does not belong to the view tree and something triggers a layout update
if (!view.nativeViewProtected) {
return;
}
const { width, height } = view.getActualSize();
const cornerRadiusDip = Utils.layout.toDeviceIndependentPixels(Length.toDevicePixels(view.borderTopLeftRadius));
const cornerRadius = Math.min(Math.min(width, height) / 2, cornerRadiusDip);
const drawableLayers = _getDrawableLayers(view);
const bounds = view.nativeViewProtected.bounds;
const shadowPath = UIBezierPath.bezierPathWithRoundedRectCornerRadius(bounds, cornerRadius).CGPath;
CATransaction.begin();
CATransaction.setDisableActions(true);
for (const layer of drawableLayers) {
layer.frame = bounds;
// Corner radius relies on size, so update it during resize
layer.cornerRadius = cornerRadius;
layer.shadowPath = shadowPath;
layer.setNeedsDisplay();
}
CATransaction.setDisableActions(false);
CATransaction.commit();
}
function _updateSublayerShadows(view, sublayers) {
const state = view.neumorphism;
if (!state) {
return;
}
const [bgLayer, fgLayer] = sublayers;
if (bgLayer == null || fgLayer == null) {
return;
}
const { width, height } = view.getActualSize();
const cornerRadiusDip = Utils.layout.toDeviceIndependentPixels(Length.toDevicePixels(view.borderTopLeftRadius));
const cornerRadius = Math.min(Math.min(width, height) / 2, cornerRadiusDip);
const fillColor = view.style.backgroundColor instanceof Color ? view.style.backgroundColor.ios.CGColor : UIColor.whiteColor.CGColor;
const shadowRadius = (view.shadowRadius || view.shadowDistance * 2) / Screen.mainScreen.scale;
const shadowOpacity = state == NeumorphicType.PRESSED ? 0 : 1;
CATransaction.begin();
CATransaction.setDisableActions(true);
bgLayer.cornerRadius = cornerRadius;
bgLayer.backgroundColor = fillColor;
bgLayer.shadowColor = view.darkShadowColor.ios.CGColor;
bgLayer.shadowOffset = CGSizeMake(view.shadowDistance, view.shadowDistance);
bgLayer.shadowRadius = shadowRadius;
bgLayer.shadowOpacity = shadowOpacity;
fgLayer.cornerRadius = cornerRadius;
fgLayer.backgroundColor = fillColor;
fgLayer.contentsScale = Screen.mainScreen.scale;
fgLayer.allowsEdgeAntialiasing = true;
fgLayer.shadowColor = view.lightShadowColor.ios.CGColor;
fgLayer.shadowOffset = CGSizeMake(-view.shadowDistance, -view.shadowDistance);
fgLayer.shadowRadius = shadowRadius;
fgLayer.shadowOpacity = shadowOpacity;
CATransaction.setDisableActions(false);
CATransaction.commit();
fgLayer.setNeedsDisplay();
}
function _onLayoutChange(args) {
_updateSublayerBounds(args.object);
}
function _onVisibilityChange(args) {
const style = args.object;
if (style.view) {
_updateSublayerBounds(style.view);
}
}
function _updateNeumorphismState(value) {
const drawableLayers = _getDrawableLayers(this);
if (value) {
if (drawableLayers.length) {
_updateSublayerShadows(this, drawableLayers);
}
else {
const nativeView = this.nativeViewProtected;
const canvas = new NeumorphicCanvas(this);
// Keep neumorphic canvas as a reference to prevent GC from getting rid of it
this.augmentedCanvas = canvas;
const bgLayer = CALayer.layer();
const fgLayer = CANeumorphicLayer.initWithCanvas(canvas);
CATransaction.begin();
CATransaction.setDisableActions(true);
bgLayer.name = 'backgroundDrawable';
bgLayer.zPosition = -2;
fgLayer.name = 'foregroundDrawable';
fgLayer.zPosition = -1;
nativeView.layer.insertSublayerAtIndex(bgLayer, 0);
nativeView.layer.insertSublayerAbove(fgLayer, bgLayer);
CATransaction.setDisableActions(false);
CATransaction.commit();
_updateSublayerShadows(this, [bgLayer, fgLayer]);
_updateSublayerBounds(this);
this.on(LayoutBase.layoutChangedEvent, _onLayoutChange);
this.style.on('visibilityChange', _onVisibilityChange);
}
}
else {
if (this.augmentedCanvas != null) {
delete this.augmentedCanvas;
}
if (drawableLayers.length) {
for (const layer of drawableLayers) {
layer.removeFromSuperlayer();
}
}
this.off(LayoutBase.layoutChangedEvent, _onLayoutChange);
this.style.off('visibilityChange', _onVisibilityChange);
}
}
// Disable 'backgroundInternal' as it also uses 'setBackground' to apply a drawable
const backgroundInternalOrigin = LayoutBase.prototype[backgroundInternalProperty.setNative];
LayoutBase.prototype[borderTopColorProperty.setNative] = _refresh;
LayoutBase.prototype[borderTopWidthProperty.setNative] = _refresh;
LayoutBase.prototype[borderTopLeftRadiusProperty.setNative] = _refresh;
LayoutBase.prototype[lightShadowColorProperty.setNative] = _refresh;
LayoutBase.prototype[darkShadowColorProperty.setNative] = _refresh;
LayoutBase.prototype[shadowDistanceProperty.setNative] = _refresh;
LayoutBase.prototype[shadowRadiusProperty.setNative] = _refresh;
LayoutBase.prototype[neumorphismProperty.setNative] = _updateNeumorphismState;
LayoutBase.prototype[backgroundInternalProperty.setNative] = function (value) {
if (this.neumorphism) {
_refresh.call(this);
}
else {
backgroundInternalOrigin.call(this, value);
}
};
//# sourceMappingURL=index.ios.js.map