blob: 0c09e5ce197b451355aa5127e47a94b542521c1c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#import "WXTransform.h"
#import "math.h"
#import "WXLength.h"
#import "WXUtility.h"
#import "WXSDKInstance.h"
#import "WXConvert.h"
#import "WXSDKEngine.h"
#import "WXConfigCenterProtocol.h"
@interface WXTransform()
@property (nonatomic, weak) WXSDKInstance *weexInstance;
@end
@implementation WXTransform
{
float _rotateAngle; //for rotate
float _scaleX;
float _scaleY;
WXLength *_translateX;
WXLength *_translateY;
WXLength *_originX;
WXLength *_originY;
//3d rotate
float _rotateX;
float _rotateY;
float _rotateZ;
float _perspective;
CATransform3D _nativeTransform;
BOOL _useNativeTransform;
}
- (instancetype)initWithCSSValue:(NSString *)cssValue origin:(NSString *)origin instance:(WXSDKInstance *)instance
{
if (self = [super init]) {
_weexInstance = instance;
_scaleX = 1.0;
_scaleY = 1.0;
_rotateX = 0.0;
_rotateY = 0.0;
_rotateZ = 0.0;
_rotateAngle = 0.0;
// default is parallel projection
_perspective = CGFLOAT_MAX;
[self parseTransform:cssValue];
[self parseTransformOrigin:origin];
}
return self;
}
- (instancetype)initWithNativeTransform:(CATransform3D)transform instance:(WXSDKInstance *)instance
{
if (self = [super init]) {
_weexInstance = instance;
_nativeTransform = transform;
_useNativeTransform = YES;
}
return self;
}
- (float)rotateAngle
{
if (_useNativeTransform) {
return atan2(_nativeTransform.m11, _nativeTransform.m12);
}
return _rotateAngle;
}
- (float)rotateX
{
if (_useNativeTransform) {
return atan2(_nativeTransform.m22, _nativeTransform.m23);
}
return _rotateX;
}
- (float)rotateY
{
if (_useNativeTransform) {
return atan2(_nativeTransform.m11, _nativeTransform.m31);
}
return _rotateY;
}
- (float)rotateZ
{
return _rotateZ;
}
- (WXLength *)translateX
{
if (_useNativeTransform) {
return [WXLength lengthWithFloat:_nativeTransform.m41 type:WXLengthTypeFixed];
}
return _translateX;
}
- (WXLength *)translateY
{
if (_useNativeTransform) {
return [WXLength lengthWithFloat:_nativeTransform.m42 type:WXLengthTypeFixed];
}
return _translateY;
}
- (float)scaleX
{
if (_useNativeTransform) {
return sqrt(_nativeTransform.m11 * _nativeTransform.m11 + _nativeTransform.m21 * _nativeTransform.m21);
}
return _scaleX;
}
- (float)scaleY
{
if (_useNativeTransform) {
return sqrt(_nativeTransform.m12 * _nativeTransform.m12 + _nativeTransform.m22 * _nativeTransform.m22);
}
return _scaleY;
}
- (void)setTransformOrigin:(NSString *)transformOriginCSS
{
[self parseTransformOrigin:transformOriginCSS];
}
- (CATransform3D)nativeTransformWithView:(UIView *)view
{
if (_useNativeTransform) {
return _nativeTransform;
}
CATransform3D nativeTransform3d = [self nativeTransformWithoutRotateWithView:view];
if (_rotateAngle != 0 || _rotateZ != 0) {
nativeTransform3d = CATransform3DRotate(nativeTransform3d, _rotateAngle?:_rotateZ, 0, 0, 1);
}
if (_rotateY != 0) {
nativeTransform3d = CATransform3DRotate(nativeTransform3d, _rotateY, 0, 1, 0);
}
if (_rotateX != 0) {
nativeTransform3d = CATransform3DRotate(nativeTransform3d, _rotateX, 1, 0, 0);
}
return nativeTransform3d;
}
- (CATransform3D)nativeTransformWithoutRotateWithView:(UIView *)view
{
CATransform3D nativeTansform3D = CATransform3DIdentity;
// CGFLOAT_MAX is not INF on 32-bit device
if(_perspective && _perspective != CGFLOAT_MAX && !isinf(_perspective)) {
nativeTansform3D.m34 = -1.0/_perspective;
}
if (!view || view.bounds.size.width <= 0 || view.bounds.size.height <= 0) {
return nativeTansform3D;
}
if (_translateX || _translateY) {
nativeTansform3D = CATransform3DTranslate(nativeTansform3D, _translateX ? [_translateX valueForMaximum:view.bounds.size.width] : 0, _translateY ? [_translateY valueForMaximum:view.bounds.size.height]:0, 0);
}
nativeTansform3D = CATransform3DScale(nativeTansform3D, _scaleX, _scaleY, 1.0);
return nativeTansform3D;
}
-(void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view
{
CGPoint newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x,
view.bounds.size.height * anchorPoint.y);
CGPoint oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x,
view.bounds.size.height * view.layer.anchorPoint.y);
newPoint = CGPointApplyAffineTransform(newPoint, view.transform);
oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform);
CGPoint position = view.layer.position;
position.x -= oldPoint.x;
position.x += newPoint.x;
position.y -= oldPoint.y;
position.y += newPoint.y;
view.layer.position = position;
view.layer.anchorPoint = anchorPoint;
}
- (void)applyTransformForView:(UIView *)view
{
if (!view || view.bounds.size.width <= 0 || view.bounds.size.height <= 0) {
return;
}
BOOL applyTransformOrigin = _originX || _originY;
if (applyTransformOrigin) {
/**
* Waiting to fix the issue that transform-origin behaves in rotation
* http://ronnqvi.st/translate-rotate-translate/
**/
CGPoint anchorPoint = CGPointMake(
_originX ? [_originX valueForMaximum:view.bounds.size.width] / view.bounds.size.width : 0.5,
_originY ? [_originY valueForMaximum:view.bounds.size.height] / view.bounds.size.height : 0.5);
[self setAnchorPoint:anchorPoint forView:view];
}
CATransform3D nativeTransform3d = [self nativeTransformWithView:view];
if (!CATransform3DEqualToTransform(view.layer.transform, nativeTransform3d)){
view.layer.transform = nativeTransform3d;
}
}
- (void)parseTransform:(NSString *)cssValue
{
if (!cssValue || cssValue.length == 0 || [cssValue isEqualToString:@"none"]) {
return;
}
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(\\w+)\\((.+?)\\)"
options:NSRegularExpressionCaseInsensitive
error:&error];
NSArray *matches = [regex matchesInString:cssValue options:0 range:NSMakeRange(0, cssValue.length)];
for (NSTextCheckingResult *match in matches) {
NSString *name = [cssValue substringWithRange:[match rangeAtIndex:1]];
NSArray *value = [[cssValue substringWithRange:[match rangeAtIndex:2]] componentsSeparatedByString:@","];
SEL method = NSSelectorFromString([NSString stringWithFormat:@"parse%@:", [name capitalizedString]]);
if ([self respondsToSelector:method]) {
@try {
id<WXConfigCenterProtocol> configCenter = [WXSDKEngine handlerForProtocol:@protocol(WXConfigCenterProtocol)];
BOOL parseTransformIfWaitUntilDone = NO;
if ([configCenter respondsToSelector:@selector(configForKey:defaultValue:isDefault:)]) {
parseTransformIfWaitUntilDone = [[configCenter configForKey:@"iOS_weex_ext_config.parseTransformIfWaitUntilDone" defaultValue:@(NO) isDefault:NULL] boolValue];
}
if (parseTransformIfWaitUntilDone) {
[self performSelectorOnMainThread:method withObject:value waitUntilDone:YES];
}
else{
IMP imp = [self methodForSelector:method];
void (*func)(id, SEL,NSArray *) = (void *)imp;
func(self, method,value);
}
}
@catch (NSException *exception) {
WXLogError(@"WXTransform exception:%@", [exception reason]);
}
}
}
}
- (void)parseTransformOrigin:(NSString *)cssValue
{
if (!cssValue || cssValue.length == 0 || [cssValue isEqualToString:@"none"]) {
return;
}
NSArray *values = [cssValue componentsSeparatedByString:@" "];
double originX = 50;
double originY = 50;
WXLengthType typeX = WXLengthTypePercent;
WXLengthType typeY = WXLengthTypePercent;
for (NSInteger i = 0; i < values.count; i++) {
NSString *value = values[i];
if ([value isEqualToString:@"left"]) {
originX = 0;
} else if ([value isEqualToString:@"right"]) {
originX = 100;
} else if ([value isEqualToString:@"top"]) {
originY = 0;
} else if ([value isEqualToString:@"bottom"]) {
originY = 100;
} else if ([value isEqualToString:@"center"]) {
if (i == 0) {
originX = 50;
} else {
originY = 50;
}
} else {
double val = [value doubleValue];
if (i == 0) {
if ([value hasSuffix:@"%"]) {
originX = val;
} else {
typeX = WXLengthTypeFixed;
originX = WXPixelScale(val, self.weexInstance.pixelScaleFactor);
}
} else {
if ([value hasSuffix:@"%"]) {
originY = val;
} else {
typeY = WXLengthTypeFixed;
originY = WXPixelScale(val, self.weexInstance.pixelScaleFactor);
}
}
}
}
_originX = [WXLength lengthWithFloat:originX type:typeX];
_originY = [WXLength lengthWithFloat:originY type:typeY];
}
- (void)parseRotate:(NSArray *)value
{
float rotateAngle = [self getAngle:value[0]];
_rotateAngle = rotateAngle;
}
- (void)parseRotatex:(NSArray *)value
{
_rotateX = [self getAngle:value[0]];
}
- (void)parseRotatey:(NSArray *)value
{
_rotateY = [self getAngle:value[0]];
}
- (void)parseRotatez:(NSArray *)value
{
_rotateZ = [self getAngle:value[0]];
}
- (void)parsePerspective:(NSArray *)value
{
_perspective = [WXConvert WXPixelType:value[0] scaleFactor:self.weexInstance.pixelScaleFactor];
if (_perspective <= 0 ) {
_perspective = CGFLOAT_MAX;
}
}
- (void)parseTranslate:(NSArray *)value
{
[self parseTranslatex:@[value[0]]];
if (value.count > 1) {
[self parseTranslatey:@[value[1]]];
}
}
- (void)parseTranslatex:(NSArray *)value
{
WXLength *translateX;
double x = [value[0] doubleValue];
if ([value[0] hasSuffix:@"%"]) {
translateX = [WXLength lengthWithFloat:x type:WXLengthTypePercent];
} else {
x = WXPixelScale(x, self.weexInstance.pixelScaleFactor);
translateX = [WXLength lengthWithFloat:x type:WXLengthTypeFixed];
}
_translateX = translateX;
}
- (void)parseTranslatey:(NSArray *)value
{
WXLength *translateY;
double y = [value[0] doubleValue];
if ([value[0] hasSuffix:@"%"]) {
translateY = [WXLength lengthWithFloat:y type:WXLengthTypePercent];
} else {
y = WXPixelScale(y, self.weexInstance.pixelScaleFactor);
translateY = [WXLength lengthWithFloat:y type:WXLengthTypeFixed];
}
_translateY = translateY;
}
- (void)parseScale:(NSArray *)value
{
double x = [value[0] doubleValue];
double y = x;
if (value.count == 2) {
y = [value[1] doubleValue];
}
_scaleX = x;
_scaleY = y;
}
- (void)parseScalex:(NSArray *)value
{
[self parseScale:@[value[0], @1]];
}
- (void)parseScaley:(NSArray *)value
{
[self parseScale:@[@1, value[0]]];
}
// Angle in radians
- (double)getAngle:(NSString *)value
{
double angle = [value doubleValue];
if ([value hasSuffix:@"deg"]) {
return [self deg2rad:angle];
} else {
return angle;
}
}
- (double)deg2rad:(double)deg
{
return deg * M_PI / 180.0;
}
@end