react-native 使用 ART 制作圖表

先看效果圖


階梯圖表.png

實現(xiàn)效果


Simulator Screen Shot - iPhone X.png

由于圖表比較簡單寒矿,有一些需要自定義的地方炒考,使用第三方框架需要熟悉接口形入,相對于自己畫熔酷,時間成本都差不多,畫出來更便于需求變更修改当娱。(第三方表格框架推薦 victory-native react-native-charts-wrapper 吃既。相對來說victory-native要好一點(diǎn),react-native-charts-wrapper由于是對原生平臺的封裝跨细,有些屬性設(shè)置效果在兩個平臺是不一致的)鹦倚。

畫圖可以使用 react-native-svg 。由于表格比較簡單冀惭,只包含線條震叙、文字、矩形色塊散休,使用 ART 已經(jīng)很方便了媒楼。

ART 的基本使用

iOS 需要先本地添加 ART
先看一下基礎(chǔ)用法


e.png
import React from 'react';
import { StyleSheet, View, ART, Dimensions } from 'react-native';
import PropTypes from 'prop-types'

const {Surface, Text, Path, Shape} = ART;

export default class Chart extends React.Component {

    static defaultProps = {
        height: 230,
        width: Dimensions.get('window').width,
    }

    static propTypes = {
        height: PropTypes.number,
        width: PropTypes.number
    }

    render() {
        const path = Path()
            .moveTo(10,40)
            .lineTo(300,40);

        const rectpath = Path()
            .moveTo(10,60)
            .lineTo(110,60)
            .lineTo(110,160)
            .lineTo(10,160)
            .close();
        return (
            <View style={styles.container}>
                <Surface width={this.props.width} height={this.props.height}>
                    <Text strokeWidth={0.5} x={16} y={0}  alignment = "left"  fill="#ccc" font={{
                        fontSize: 12,
                        fontFamily: 'Arial',
                        fontWeight: 100,
                    }}>年化利率(%)</Text>
                    <Shape d={path} stroke={'red'} strokeWidth={1} />
                    <Shape d={rectpath} fill={'#000'} stroke={'red'} strokeWidth={1} />
                </Surface>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        backgroundColor: '#fff'
    },
});

畫圖

import React from 'react';
import { StyleSheet, View, ART, Dimensions } from 'react-native';
import PropTypes from 'prop-types'
const {Surface, Path, Shape} = ART;

const mTop = 30;
const mLeft = 50;
const mBottom = 40;
const mRight = 20

export default class Chart extends React.Component {

    static defaultProps = {
        height: 230,
        width: Dimensions.get('window').width,
        data: {
            step_info: [
                {
                    "x":"鎖定期1",
                    "y":"12.00"
                },
                {
                    "x":"鎖定期2",
                    "y": "12.50"
                },
                {
                    "x":"鎖定期3",
                    "y": "13.00"
                },
                {
                    "x":"鎖定期4",
                    "y": "13.50"
                },
                {
                    "x":"鎖定期5",
                    "y": "14.00"
                },
                {
                    "x":"鎖定期6",
                    "y": "14.50"
                }
            ],
            apr: '12',
            step_apr: '0.5',
            apr_max: '14.5',
            reward_apr: '0',
            first_apr: '0'
        }
    }

    static propTypes = {
        height: PropTypes.number,
        width: PropTypes.number
    }

    render() {
        const {width, height} = this.props
        let chartWidth = width-mRight-mLeft
        let chartHeight = height-mBottom-mTop
        const path = Path()
            .moveTo(mLeft,mTop)
            .lineTo(mLeft+chartWidth,mTop)
            .lineTo(mLeft+chartWidth,mTop+chartHeight)
            .lineTo(mLeft,mTop+chartHeight)
            .close();
        return (
            <View style={styles.container}>
                <Surface width={width} height={height}>
                    <ART.Text strokeWidth={0.5} x={16} y={0} fill="#ccc" font={{
                        fontSize: 14,
                        fontFamily: 'Arial',
                        fontWeight: 'normal',
                    }}>年化利率(%)</ART.Text>
                    <ART.Text strokeWidth={0.5} alignment={'right'} x={mLeft+chartWidth} y={mTop+chartHeight+20} fill="#ccc" font={{
                        fontSize: 14,
                        fontFamily: 'Arial',
                        fontWeight: 'normal',
                    }}>持有時長(天數(shù))</ART.Text>
                    <Shape d={path} stroke={'ccc'} strokeWidth={1} />
                    {this._renderChartData()}
                </Surface>
            </View>
        );
    }

    _renderChartData(){
        const {width, height} = this.props
        let chartWidth = width-mRight-mLeft
        let chartHeight = height-mBottom-mTop

        let dataWidth = chartWidth/this.props.data.step_info.length

        let reverseInfo = this.props.data.step_info.slice().reverse()
        let maxApr = parseFloat(this.props.data.apr_max)
        let minApr = parseFloat(this.props.data.apr) - parseFloat(this.props.data.step_apr)
        let lineCount = (maxApr - minApr) / parseFloat(this.props.data.step_apr)
        let step_height = chartHeight/(lineCount)

        let result = this.props.data.step_info.map((e, i)=>{
            let lines = []

            //
            let minValue = parseFloat(this.props.data.apr)-parseFloat(this.props.data.step_apr)
            let x = mLeft+dataWidth * i
            let y =mTop + (parseFloat(reverseInfo[i].y)-minValue) / (parseFloat(this.props.data.apr_max) -minValue) *chartHeight-step_height
            let infoText = `${e.y}`
            if (i===0 && parseFloat(this.props.data.first_apr) > 0) {
                y = mTop + (parseFloat(reverseInfo[i].y)-parseFloat(this.props.data.first_apr)-minValue) / (parseFloat(this.props.data.apr_max) -minValue) *chartHeight-step_height
                infoText = `${e.y}+${this.props.data.first_apr}`
            } else if ( parseFloat(this.props.data.reward_apr) > 0 ){
                infoText = `${e.y}+${this.props.data.reward_apr}`
            }
            let dataHeight = chartHeight - y + mTop
            const path = ART.Path()
                .moveTo(mLeft,mTop+(i)*step_height)
                .lineTo(mLeft+chartWidth,mTop+(i)*step_height)
            // girdLine
            lines.push(<Shape d={path} stroke="#ccc" strokeWidth={0.5} />)

            //xaxis
            lines.push(
                this._renderXLabel(x+0.5*dataWidth, mTop+chartHeight+3, e.x)
            )

            // top line
            lines.push(
                this._renderBarTopLine(x, y, dataWidth)
            )

            // info
            lines.push(
                this._renderInfoLabel(x+0.5*dataWidth, y-14, infoText)
            )

            // rect
            lines.push(
                this._renderAData(x, y, dataWidth, dataHeight)
            )

            return lines
        })


        // yaxis
        let ys = []
        for (let i = maxApr; i > minApr; i-=parseFloat(this.props.data.step_apr)) {
            let yValue = i
            ys.push(
                this._renderYLabel(mLeft-20, mTop+(1-(i-minApr)/(maxApr-minApr))*chartHeight-7, yValue+parseFloat(this.props.data.reward_apr))
            )
        }
        result = result.concat(ys)

        return result
    }

    _renderYLabel(x, y, value){
        return (
            <ART.Text strokeWidth={1}
                      x={x}
                      y={y}
                      fill="#ccc"
                      font={{
                          fontSize: 12,
                          fontFamily: 'Arial',
                          fontWeight: 100
                      }}
                      alignment='center'
            >{this._formatChartApr(value)}</ART.Text>
        )
    }

    _renderXLabel(x, y, value){
        return (
            <ART.Text strokeWidth={1}
                      x={x}
                      y={y}
                      fill="#ccc"
                      font={{
                          fontSize: 12,
                          fontFamily: 'Arial',
                          fontWeight: 100
                      }}
                      alignment='center'
            >{value}</ART.Text>
        )
    }

    _renderBarTopLine(x, y, width){
        const path = ART.Path()
            .moveTo(x,y)
            .lineTo(x+width,y)
        return <Shape d={path} stroke="#FF6E00" strokeWidth={2} />
    }

    _renderInfoLabel(x, y, value){
        return (
            <ART.Text strokeWidth={1}
                      x={x}
                      y={y}
                      fill="#FF6E00"
                      font={{
                          fontSize: 10,
                          fontFamily: 'Arial',
                          fontWeight: 100
                      }}
                      alignment='center'
            >{value}</ART.Text>
        )
    }

    _renderAData(x, y, width, height){
        const path = new ART.Path()
            .moveTo(x, y)
            .lineTo(x+width, y)
            .lineTo(x+width,y+height)
            .lineTo(x,y+height)
            .close();

        return  <ART.Shape d={path} fill={'#FF6E0040'}/>

    }


    _formatChartApr(apr){
        return parseFloat(apr).toFixed(2).toString()
    }
}

const styles = StyleSheet.create({
    container: {
        backgroundColor: '#fff'
    },
});

GitHub 地址

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市戚丸,隨后出現(xiàn)的幾起案子划址,更是在濱河造成了極大的恐慌,老刑警劉巖限府,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件夺颤,死亡現(xiàn)場離奇詭異,居然都是意外死亡胁勺,警方通過查閱死者的電腦和手機(jī)世澜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來姻几,“玉大人宜狐,你說我怎么就攤上這事势告。” “怎么了抚恒?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵咱台,是天一觀的道長。 經(jīng)常有香客問我俭驮,道長回溺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任混萝,我火速辦了婚禮遗遵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逸嘀。我一直安慰自己车要,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布崭倘。 她就那樣靜靜地躺著翼岁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪司光。 梳的紋絲不亂的頭發(fā)上琅坡,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天,我揣著相機(jī)與錄音残家,去河邊找鬼榆俺。 笑死,一個胖子當(dāng)著我的面吹牛坞淮,可吹牛的內(nèi)容都是我干的茴晋。 我是一名探鬼主播,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼回窘,長吁一口氣:“原來是場噩夢啊……” “哼晃跺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起毫玖,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎凌盯,沒想到半個月后付枫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡驰怎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年阐滩,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片县忌。...
    茶點(diǎn)故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡掂榔,死狀恐怖继效,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情装获,我是刑警寧澤瑞信,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站穴豫,受9級特大地震影響凡简,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜精肃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一秤涩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧司抱,春花似錦筐眷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至津畸,卻和暖如春振定,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背肉拓。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工后频, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人暖途。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓卑惜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親驻售。 傳聞我的和親對象是個殘疾皇子露久,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明先生_X自主閱讀 15,979評論 3 119
  • React Native開發(fā)中常用三方組件大全 作者整理的一套常用的React Native開發(fā)中使用到的三方組件...
    光強(qiáng)_上海閱讀 21,179評論 6 95
  • 那時候欺栗,她的男友在車間工作毫痕。她第一次去那里找他,赴晚間的約會迟几。 她沒有立刻走進(jìn)車間消请,而是愣在了門口———車間的墻上...
    秋未完閱讀 567評論 2 9
  • 生命是一條小河臊泰,你就像一艘小舟,命運(yùn)就是撐船的船夫蚜枢。 生命是一棵蘋果 樹缸逃,而你针饥,就像那蘋果樹的種子。命運(yùn)就是它的果...
    Qiu_Naruto閱讀 184評論 0 1
  • 尊敬的公司領(lǐng)導(dǎo): 我是________部門的________需频,于______年_____月______日成為公司的...
    莊唯1閱讀 191評論 0 0