react-native-painter
Version:
Custom Painter Android WEB IOS
442 lines (364 loc) • 18.2 kB
JavaScript
import React, { useEffect, useLayoutEffect, useRef } from 'react'
import Attributes from 'react-native-painter/src/Components/Constants'
const clamp = (num) =>{
return num < 0 ? 0 : (num > 1 ? 1 : num)
}
//text-after-edge -> descender
//text-before-edge-> ascender
//hanging -> capheight
//central -> center
//middle -> middle
//none -> ""
const getBaselineWeb = (b)=>{
var result = ""
switch(b){
case "descender":
result = "text-after-edge"
break
case "ascender":
result = "text-before-edge"
break
case "capHeight":
result = "hanging"
break
case "center":
result = "central"
break
case "middle":
result = "middle"
break
default:
result = ""
break
}
return result
}
const TextWeb = React.forwardRef((props,ref)=>{
const myRef = ref ? ref : useRef()
const {
x,
y,
text,
font,
fontSize,
fontStyle,
baseline,
textAnchor,
verticalOffset,
horizontalOffset,
isChildMask,
painterKey,
viewBox,
align,
aspect,
fill,
fillOpacity,
stroke,
strokeOpacity,
strokeWidth,
shadow,
shadowRadius,
shadowOffset,
shadowOffsetX,
shadowOffsetY,
shadowPercentageValue,
shadowRect,
mask,
opacity,
translateZ,
transX,
transY,
transPercentageValue,
rot,
rotO,
rotOx,
rotOy,
rotPercentageValue,
sc,
scX,
scY,
scO,
scOx,
scOy,
scPercentageValue,
style,
...rest
} = props
//MARK: Props
const lx = x === undefined ? 0 : x
const ly = y === undefined ? 0 : y
const content = text === undefined ? "" : text
const typeface = font === undefined ? "" : font
const fontStyl = fontStyle === 'italic' ? "italic" : "normal"
const fontWeg = fontStyle === "bold" ? "bold" : "normal"
const fontSiz = fontSize === undefined ? 12 : fontSize
const basel = baseline === undefined ? "" : getBaselineWeb(baseline)
const textAn = textAnchor === undefined ? "" : textAnchor
const isMasking = isChildMask === undefined ? false : isChildMask
//MARK : Paintable
const fc = fill === undefined ? '' : fill
const fo = fillOpacity === undefined ? "" : `${fillOpacity}`
const stc = stroke === undefined ? '' : stroke
const so = strokeOpacity === undefined ? "" : `${strokeOpacity}`
const sw = strokeWidth === undefined ? 1 : strokeWidth
const shc = shadow === undefined ? 'rgba(0,0,0,0)'.split(",") : shadow.split(",")
const shr = shadowRadius === undefined ? 2 : shadowRadius
const shox = shadowOffset === undefined ? (shadowOffsetX === undefined ? 2 : shadowOffsetX ) : shadowOffset
const shoy = shadowOffset === undefined ? (shadowOffsetY === undefined ? 2 : shadowOffsetY) : shadowOffset
const shRect = shadowRect === undefined ? {x:-2, y:-2 , w:5,h:5, units:'objectBoundingBox'} : shadowRect
const mk = mask === undefined ? "" : mask
const op = opacity === undefined ? "" : `${opacity}`
const dx = transX === undefined ? 0 : transX
const dy = transY === undefined ? 0 : transY
const scaleX = sc === undefined ? ( scX === undefined ? 1 : scX) : sc
const scaleY = sc === undefined ? (scY === undefined ? 1 : scY) : sc
const scaleOX = scO === undefined ? ( scOx === undefined ? 0 : scOx) : scO
const scaleOY = scO === undefined ? (scOy === undefined ? 0 : scOy) : scO
const rotation = rot === undefined ? 0 : rot
const rotationOX = rotO === undefined ? ( rotOx === undefined ? 0 : rotOx) : rotO
const rotationOY = rotO === undefined ? (rotOy === undefined ? 0 : rotOy) : rotO
const transform = `rotate(${rotation} ${rotationOX} ${rotationOY}) translate(${scaleOX} ${scaleOY}) scale(${scaleX} ${scaleY}) translate(${-scaleOX} ${-scaleOY}) translate(${dx} ${dy})`
const userKey = painterKey === undefined ? "" : painterKey
const keyElemet = Attributes.elements.prefix
const keyFilter = `${keyElemet}${Attributes.elements.filter}${userKey}`
const filterShadowProp = `url(#${keyFilter})`
const isFillTransparent = fc === `rgba(0, 0, 0, 0)` || fc === "transparent"
return(
<g ref={myRef} opacity={op} mask={`url(#${mk})`} transform={transform}
curtransform={`${rotation} ${rotationOX} ${rotationOY} ${scaleX} ${scaleY} ${scaleOX} ${scaleOY} ${dx} ${dy}`}
>
<svg style={{width:'100%',height:'100%',overflow:'visible'}} viewBox={viewBox}
preserveAspectRatio={`${align} ${aspect}`}>
<defs>
<filter id={keyFilter}
filterUnits={`${shRect.units}`}
x={`${shRect.x}`} y={`${shRect.y}`} width={`${shRect.w}`} height={`${shRect.h}`}>
<feDropShadow
id={`${keyElemet}${Attributes.elements.shadow}${userKey}`}
dx={shox} dy={shoy} stdDeviation={shr}
floodColor={shc}
/>
</filter>
</defs>
<text id={`${keyElemet}${Attributes.elements.fill}${userKey}`}
x={lx} y={ly} fontFamily={typeface} fontSize={fontSiz} fontStyle={fontStyl} fontWeight={fontWeg} textAnchor={textAn}
dominantBaseline={basel}
filter={!isFillTransparent ? filterShadowProp : ""}
fill={fc} fillOpacity={fo}
stroke={"none"}
>
{content}
</text>
<g id={`${keyElemet}${Attributes.elements.gShadow}${userKey}`}
filter={isFillTransparent ? filterShadowProp : ""} >
<text id={`${keyElemet}${Attributes.elements.stroke}${userKey}`}
x={lx} y={ly} fontFamily={typeface} fontSize={fontSiz} fontStyle={fontStyl} fontWeight={fontWeg} textAnchor={textAn}
dominantBaseline={basel}
fill={"none"}
stroke={stc} strokeOpacity={so}
strokeWidth={sw}
>
{content}
</text>
</g>
</svg>
</g>
)
})
export default class Text extends React.PureComponent{
constructor(props){
super(props)
this.keyElemet = Attributes.elements.prefix
this.myRef = React.createRef()
this.setNativeProps = this.setNativeProps.bind(this)
this.updateProp = this.updateProp.bind(this)
this.handleProps = this.handleProps.bind(this)
this.handleCommonProps = this.handleCommonProps.bind(this)
this.handleShadow = this.handleShadow.bind(this)
this.updateAttribute = this.updateAttribute.bind(this)
this.getAttribute = this.getAttribute.bind(this)
this.getElement = this.getElement.bind(this)
}
setNativeProps(object){
if(this.myRef.current && this.props.painterKey){
let keys = Object.keys(object)
for(var i = 0; i < keys.length;i++){
let k = keys[i]
let value = object[k]
this.updateProp(k,value)
}
let isStyle = object.hasOwnProperty('style')
if(isStyle){
let keysStyle = Object.keys(object.style)
for(var i = 0; i < keysStyle.length;i++){
let k = keysStyle[i]
let value = object.style[k]
this.updateProp(k,value)
}
}
}
}
updateProp(k,value){
let isProp = this.handleProps(k,value)
if(!isProp){
this.handleCommonProps(k,value)
}
}
handleProps(k,value){
switch(k){
case "text":
let f = this.getElement(Attributes.elements.fill)
let s = this.getElement(Attributes.elements.stroke)
if(f) f.textContent = value
if(s) s.textContent = value
return true;
case "x":
case "y":
case "font":
case "fontSize":
case "textAnchor":
this.updateAttribute(Attributes[k],value,this.getElement(Attributes.elements.fill))
this.updateAttribute(Attributes[k],value,this.getElement(Attributes.elements.stroke))
return true;
case "fontStyle":
if(value === 'bold') {
this.updateAttribute(Attributes.fontWeight,value,this.getElement(Attributes.elements.fill))
this.updateAttribute(Attributes.fontStyle,'normal',this.getElement(Attributes.elements.fill))
this.updateAttribute(Attributes.fontWeight,value,this.getElement(Attributes.elements.stroke))
this.updateAttribute(Attributes.fontStyle,'normal',this.getElement(Attributes.elements.stroke))
}else{
this.updateAttribute(Attributes.fontWeight,'normal',this.getElement(Attributes.elements.fill))
this.updateAttribute(Attributes.fontStyle,value,this.getElement(Attributes.elements.fill))
this.updateAttribute(Attributes.fontWeight,'normal',this.getElement(Attributes.elements.stroke))
this.updateAttribute(Attributes.fontStyle,value,this.getElement(Attributes.elements.stroke))
}
return true;
case "baseline":
this.updateAttribute(Attributes[k],getBaselineWeb(value),this.getElement(Attributes.elements.fill))
this.updateAttribute(Attributes[k],getBaselineWeb(value),this.getElement(Attributes.elements.stroke))
return true;
}
return false
}
handleCommonProps(k,value){
if(k.includes('fill')){
if(k === 'fill'){
let keyFilter = `${this.keyElemet}${Attributes.elements.filter}${this.props.painterKey}`
let filterShadowProp = `url(#${keyFilter})`
let isFillTransparent = value === `rgba(0, 0, 0, 0)` || value === "transparent"
let elFill = this.getElement(Attributes.elements.fill)
this.updateAttribute(Attributes[k],value,elFill)
this.updateAttribute(Attributes.filter,!isFillTransparent ? filterShadowProp : "",elFill)
this.updateAttribute(Attributes.filter,isFillTransparent ? filterShadowProp : "",this.getElement(Attributes.elements.gShadow))
}else{
this.updateAttribute(Attributes[k],value,this.getElement(Attributes.elements.fill))
}
}else if(k.includes('stroke')){
this.updateAttribute( Attributes[k],value,this.getElement(Attributes.elements.stroke))
}else if(k.includes('shadow')){
this.handleShadow(k,value)
}else if(k === 'opacity'){
this.updateAttribute(Attributes.opacity,value,this.myRef.current)
}else if(k === 'mask'){
this.updateAttribute(Attributes.mask,`url(#${value})`,this.myRef.current)
}//transform
else{
var container = this.myRef.current
let t = this.getAttribute(Attributes.curTransform,container).split(' ')
var valueT = ""
var currT = ""
switch(k){
case "transX":
valueT = `rotate(${t[0]} ${t[1]} ${t[2]}) translate(${t[5]} ${t[6]}) scale(${t[3]} ${t[4]}) translate(${-t[5]} ${-t[6]}) translate(${value} ${t[8]})`
currT = `${t[0]} ${t[1]} ${t[2]} ${t[3]} ${t[4]} ${t[5]} ${t[6]} ${value} ${t[8]}`
break;
case "transY":
valueT = `rotate(${t[0]} ${t[1]} ${t[2]}) translate(${t[5]} ${t[6]}) scale(${t[3]} ${t[4]}) translate(${-t[5]} ${-t[6]}) translate(${t[7]} ${value})`
currT = `${t[0]} ${t[1]} ${t[2]} ${t[3]} ${t[4]} ${t[5]} ${t[6]} ${t[7]} ${value}`
break;
case "rot":
valueT = `rotate(${value} ${t[1]} ${t[2]}) translate(${t[5]} ${t[6]}) scale(${t[3]} ${t[4]}) translate(${-t[5]} ${-t[6]}) translate(${t[7]} ${t[8]})`
currT = `${value} ${t[1]} ${t[2]} ${t[3]} ${t[4]} ${t[5]} ${t[6]} ${t[7]} ${t[8]}`
break;
case "rotO":
valueT = `rotate(${t[0]} ${value} ${value}) translate(${t[5]} ${t[6]}) scale(${t[3]} ${t[4]}) translate(${-t[5]} ${-t[6]}) translate(${t[7]} ${t[8]})`
currT = `${t[0]} ${value} ${value} ${t[3]} ${t[4]} ${t[5]} ${t[6]} ${t[7]} ${t[8]}`
break;
case "rotOx":
valueT = `rotate(${t[0]} ${value} ${t[2]}) translate(${t[5]} ${t[6]}) scale(${t[3]} ${t[4]}) translate(${-t[5]} ${-t[6]}) translate(${t[7]} ${t[8]})`
currT = `${t[0]} ${value} ${t[2]} ${t[3]} ${t[4]} ${t[5]} ${t[6]} ${t[7]} ${t[8]}`
break;
case "rotOy":
valueT = `rotate(${t[0]} ${t[1]} ${value}) translate(${t[5]} ${t[6]}) scale(${t[3]} ${t[4]}) translate(${-t[5]} ${-t[6]}) translate(${t[7]} ${t[8]})`
currT = `${t[0]} ${t[1]} ${value} ${t[3]} ${t[4]} ${t[5]} ${t[6]} ${t[7]} ${t[8]}`
break;
case "sc":
valueT = `rotate(${t[0]} ${t[1]} ${t[2]}) translate(${t[5]} ${t[6]}) scale(${value} ${value}) translate(${-t[5]} ${-t[6]}) translate(${t[7]} ${t[8]})`
currT = `${t[0]} ${t[1]} ${t[2]} ${value} ${value} ${t[5]} ${t[6]} ${t[7]} ${t[8]}`
break;
case "scX":
valueT = `rotate(${t[0]} ${t[1]} ${t[2]}) translate(${t[5]} ${t[6]}) scale(${value} ${t[4]}) translate(${-t[5]} ${-t[6]}) translate(${t[7]} ${t[8]})`
currT = `${t[0]} ${t[1]} ${t[2]} ${value} ${t[4]} ${t[5]} ${t[6]} ${t[7]} ${t[8]}`
break;
case "scY":
valueT = `rotate(${t[0]} ${t[1]} ${t[2]}) translate(${t[5]} ${t[6]}) scale(${t[3]} ${value}) translate(${-t[5]} ${-t[6]}) translate(${t[7]} ${t[8]})`
currT = `${t[0]} ${t[1]} ${t[2]} ${t[3]} ${value} ${t[5]} ${t[6]} ${t[7]} ${t[8]}`
break;
case "scO":
valueT = `rotate(${t[0]} ${t[1]} ${t[2]}) translate(${value} ${value}) scale(${t[3]} ${t[4]}) translate(${-value} ${-value}) translate(${t[7]} ${t[8]})`
currT = `${t[0]} ${t[1]} ${t[2]} ${t[3]} ${t[4]} ${value} ${value} ${t[7]} ${t[8]}`
break;
case "scOx":
valueT = `rotate(${t[0]} ${t[1]} ${t[2]}) translate(${value} ${t[6]}) scale(${t[3]} ${t[4]}) translate(${-value} ${-t[6]}) translate(${t[7]} ${t[8]})`
currT = `${t[0]} ${t[1]} ${t[2]} ${t[3]} ${t[4]} ${value} ${t[6]} ${t[7]} ${t[8]}`
break;
case "scOy":
valueT = `rotate(${t[0]} ${t[1]} ${t[2]}) translate(${t[5]} ${value}) scale(${t[3]} ${t[4]}) translate(${-t[5]} ${-value}) translate(${t[7]} ${t[8]})`
currT = `${t[0]} ${t[1]} ${t[2]} ${t[3]} ${t[4]} ${t[5]} ${value} ${t[7]} ${t[8]}`
break;
}
if(currT.length > 0 && valueT.length > 0 ){
this.updateAttribute(Attributes.transform,valueT,container)
this.updateAttribute(Attributes.curTransform,currT,container)
}
}
}
handleShadow(k,value){
switch(k){
case "shadow":
case "shadowRadius":
case "shadowOffsetX":
case "shadowOffsetY":
this.updateAttribute(Attributes[k],value,this.getElement(Attributes.elements.shadow))
break;
case "shadowOffset":
this.updateAttribute(Attributes.shadowOffsetX,value,this.getElement(Attributes.elements.shadow))
this.updateAttribute(Attributes.shadowOffsetY,value,this.getElement(Attributes.elements.shadow))
break;
case "shadowRect":
let e = this.getElement(Attributes.elements.filter)
this.updateAttribute(Attributes.shadowRect.units,value.units,e)
this.updateAttribute(Attributes.shadowRect.x,value.x,e)
this.updateAttribute(Attributes.shadowRect.y,value.y,e)
this.updateAttribute(Attributes.shadowRect.w,value.w,e)
this.updateAttribute(Attributes.shadowRect.h,value.h,e)
break
}
}
getElement(id){
return this.myRef.current.querySelector(`#${this.keyElemet}${id}${this.props.painterKey}`)
}
updateAttribute(k,value,element){
if(element) element.setAttribute(k,value)
}
getAttribute(k,element){
if(element) {
let v = element.getAttribute(k)
return v ? v : ""
}
return ""
}
render(){
return <TextWeb ref={this.myRef} {...this.props} />
}
}