怎么在可视化编辑器中局部更新元素样式


/**
 * 遍历子节点数据
 */
export const iterateComponentData = (() => {
  const iterator = (fn, childrenData) => {
    for (let i = 0; i < childrenData.length; i++) {
      const childData = childrenData[i]
      if (!childData) { continue }
      const flag = fn(childData, childrenData)
      if (flag) { return }
      if (childData.childrenData && childData.childrenData.length > 0) {
        iterator(fn, childData.childrenData)
      }
    }
  }
  return (
    fn,
    childrenData
  ) => {
    iterator(fn, childrenData)
  }
})()

export const createTag = ({ type = 'style', key, content }) => {
  const dom = document.getElementById(key) || null
  const target = dom || document.createElement(type)
  type === 'style' && target.setAttribute('type', 'text/css')
  target.id = key
  target.innerHTML = content
  !dom && document.head.appendChild(target)
  // delay(() => { target.setAttribute('title', key) }, 10)
}
import { kebabCase, cloneDeep } from 'lodash'

const cssKey = 'editing-css'
const jsKey = 'editing-js'

export const OperaType = {
  add: 0,
  clear: 1,
  update: 2
}

const deal = {
  // 拼接元素样式
  style ({ className, style }) {
    let itemCss = ''
    for (const key in style) {
      itemCss += `${kebabCase(key)}: ${style[key]};`
    }
    itemCss = `${className}{${itemCss}}`
    return itemCss
  },
  // 拼接编辑器元素单位子元素,伪元素,交互样式
  interact ({ className, interact }) {
    let interactText = ''
    for (const key in interact) {
      if (key) {
        const isPse = /^:/.test(key)
        const insterName = isPse ? `${className}${key}` : `${className} ${key}`

        interactText += deal.style({ className: insterName, style: interact[key] })
      }
    }

    return interactText
  },
  // 整理媒体查询的相关数据
  media ({ className, media, mediaObj = {} }) {
    for (const key in media) {
      const mediaInteract = cloneDeep(media[key])
      delete mediaInteract.normal
      const mediaCss = deal.style({ className, style: media[key].normal }) + deal.interact({ className, interact: mediaInteract })
      mediaObj[key] = mediaObj[key] ? `${mediaObj[key]}${mediaCss}` : mediaCss
    }

    return mediaObj
  },
  script ({ className, script, isInit }) {
    // const str = script.replace(/%blockID%/g, className)
    const str = script ? `\n// ${className} start\n ${script.replace(/%blockID%/g, className)}\n// ${className} end` : ''
    const oriJsDom = document.querySelector(`[id^='${jsKey}']`)
    if (!isInit && oriJsDom) {
      let allScriptStr = oriJsDom.innerHTML
      console.log(allScriptStr, 'allScriptStrallScriptStrallScriptStr')
      const rex = new RegExp(`(?<=\\/\\/\\s+${className}\\s+start)[\\s\\S]*(?=\\/\\/\\s+${className}\\s+end)`, 'g')
      if (rex.test(allScriptStr)) {
        allScriptStr.replace(rex, str)
      } else {
        allScriptStr += str
      }

      oriJsDom && oriJsDom.remove()

      setTimeout(() => { createTag({ key: `${jsKey}${new Date().getTime()}`, content: allScriptStr, type: 'script' }) }, 1000)
    }
    return str
  },
  delCurrCssFromSheet ({ currClass, styleSheet }) {
    const cssRulesList = styleSheet?.cssRules ? Array.from(styleSheet.cssRules) : []
    // 记录到这个样式存在样式表中的位置
    // let currIndex = 0
    for (let i = cssRulesList.length - 1; i >= 0; i--) {
      if (cssRulesList[i].selectorText?.includes(currClass)) {
        // !currIndex && (currIndex = i)
        styleSheet.deleteRule(i)
      }
    }

    // return currIndex
  }
}
export const updateEditCss = (editData, type = OperaType.update) => {
  const currClass = `.${editData.code}-${editData.id}`
  const styleSheet = document.getElementById(cssKey)?.sheet || []
  const mediaStyleSheet = document.getElementById(`${cssKey}-media`)?.sheet || []
  const mediaStyleSheetList = mediaStyleSheet?.cssRules ? Array.from(mediaStyleSheet.cssRules) : []

  /* const styleIndex = */deal.delCurrCssFromSheet({ styleSheet, currClass })

  // key 值是媒体值,value 是对应媒体下的rule 的元素index
  // const mediaIndex = {}
  if (type !== OperaType.add) {
    for (const key in editData.media) {
      const sheetIndex = mediaStyleSheetList.findIndex(item => item.conditionText === key)
      if (sheetIndex !== -1) {
        /* mediaIndex[key] = */deal.delCurrCssFromSheet({ styleSheet: mediaStyleSheetList[sheetIndex], currClass })
      }
    }
    document.getElementById(currClass) && document.getElementById(currClass).remove()
  }

  // console.log(mediaStyleSheet, mediaIndex, 'mediaStyleSheet')

  if (type !== OperaType.clear) {
    // 处理元素本身和其子节点,伪元素,交互样式
    const itemInteract = deal.interact({ className: currClass, interact: editData.interact })
    console.log(itemInteract, 'itemInteractitemInteractitemInteract')
    const iarr = itemInteract.split('}')
    iarr.forEach(rule => {
      rule && styleSheet.insertRule(`${rule}}`, 0)
    })
    const itemStyle = deal.style({ className: currClass, style: editData.style })
    styleSheet.insertRule(itemStyle, 0)

    // 媒体查询的数据更新
    const itemMediaObj = deal.media({ className: currClass, media: editData.media })
    for (const mediaKey in editData.media) {
      const index = mediaStyleSheetList.findIndex(item => item.conditionText === mediaKey)
      const mediaText = itemMediaObj[mediaKey]
      if (index >= 0) {
        const arr = mediaText.split('}')
        arr.forEach(rule => {
          rule && mediaStyleSheetList[index].insertRule(`${rule}}`, 0)
        })
      } else {
        // 这里还需要处理媒体查询的排序问题(应该会在媒体查询需要被作为属性编辑的时候需要处理)
        mediaStyleSheet.insertRule(`@media ${mediaKey}{${mediaText}}`, 0)
      }
    }
  }
  deal.script({ className: currClass, script: editData.script || '' })
}

export const editingCss = (data, isPreview) => {
  let cssText = ''
  let jsText = ''
  const mediaObj = {}
  iterateComponentData(function(item) {
    const currClass = `.${item.code}-${item.id}`
    item.style && (cssText += deal.style({ className: currClass, style: item.style }))

    item.interact && (cssText += deal.interact({ className: currClass, interact: item.interact }))

    item.media && deal.media({ className: currClass, media: item.media, mediaObj })

    item.script && (jsText += deal.script({ className: currClass, script: item.script, isInit: true }))

    cssText += '\n'
  }, data.pages?.[0]?.childrenData || [data] || [])

  // 拼接媒体查询css
  // 这里还需要处理媒体查询的排序问题
  let mediaText = ''
  for (const me in mediaObj) {
    mediaText += `@media ${me}{${mediaObj[me]}}`
  }

  if (isPreview) {
    return {
      css: [cssText, mediaText],
      js: jsText
    }
  } else {
    createTag({ key: cssKey, content: cssText })
    createTag({ key: `${cssKey}-media`, content: mediaText })
    // js 需要延迟生效
    setTimeout(() => { createTag({ key: jsKey, content: jsText, type: 'script' }) }, 1000)
  }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值